Qual è un buon caso d'uso per l'importazione statica di metodi?


137

Ho appena ricevuto un commento di recensione secondo cui la mia importazione statica del metodo non era una buona idea. L'importazione statica era di un metodo da una classe DA, che ha principalmente metodi statici. Quindi, nel mezzo della logica aziendale, ho avuto un'attività che apparentemente apparteneva alla classe attuale:

import static some.package.DA.*;
class BusinessObject {
  void someMethod() {
    ....
    save(this);
  }
} 

Il revisore non desiderava che io cambiassi il codice e non l'ho fatto, ma sono abbastanza d'accordo con lui. Uno dei motivi addotti per non importare staticamente era che confondeva il modo in cui era stato definito il metodo, che non era nella classe corrente e non in nessuna superclasse, quindi è troppo tempo per identificarne la definizione (il sistema di revisione basato sul web non è cliccabile link come IDE :-) Non penso davvero che questo importi, le importazioni statiche sono ancora abbastanza nuove e presto ci abitueremo a localizzarle.

Ma l'altro motivo, quello con cui sono d'accordo, è che una chiamata di metodo non qualificata sembra appartenere all'oggetto corrente e non dovrebbe saltare contesti. Ma se davvero appartenesse, avrebbe senso estendere quella super classe.

Così, quando ha senso per i metodi di importazione statici? Quando l'hai fatto? Ti è piaciuto / ti piace l'aspetto delle chiamate non qualificate?

EDIT: L'opinione popolare sembra essere quella dei metodi di importazione statica se nessuno li confonderà come metodi della classe corrente. Ad esempio metodi da java.lang.Math e java.awt.Color. Ma se abs e getAlpha non sono ambigui, non vedo perché readEmployee lo sia. Come in molte scelte di programmazione, penso che anche questa sia una preferenza personale.

Grazie per la vostra risposta ragazzi, sto chiudendo la domanda.


2
Ecco un ottimo uso delle importazioni statiche: ibm.com/developerworks/library/j-ft18
intrepidis,

1
@ mr5 la sintassi è import static, la funzione èstatic import
Miserabile variabile

Risposte:


150

Questo è dalla guida di Sun quando hanno pubblicato la funzione (enfasi nell'originale):

Quindi, quando dovresti usare l'importazione statica? Con molta parsimonia! Utilizzalo solo quando altrimenti verrai tentato di dichiarare copie locali di costanti o di abusare dell'eredità (Antipattern di Constant Interface). ... Se usi eccessivamente la funzione di importazione statica, può rendere il tuo programma illeggibile e non realizzabile, inquinando il suo spazio dei nomi con tutti i membri statici che importi. I lettori del tuo codice (incluso te, pochi mesi dopo averlo scritto) non sapranno da quale classe provenga un membro statico. L'importazione di tutti i membri statici da una classe può essere particolarmente dannosa per la leggibilità; se hai bisogno solo di uno o due membri, importali singolarmente.

( https://docs.oracle.com/javase/8/docs/technotes/guides/language/static-import.html )

Ci sono due parti che voglio chiamare in particolare:

  • Usa le importazioni statiche solo quando sei stato tentato di "abusare dell'eredità". In questo caso, saresti stato tentato di avere BusinessObject extend some.package.DA? In tal caso, le importazioni statiche possono essere un modo più pulito di gestirlo. Se non avresti mai sognato di estenderlo some.package.DA, probabilmente questo è un cattivo uso delle importazioni statiche. Non usarlo solo per salvare alcuni caratteri durante la digitazione.
  • Importa singoli membri. Dire import static some.package.DA.saveinvece di DA.*. Ciò renderà molto più facile trovare la provenienza di questo metodo importato.

Personalmente, ho usato questa funzionalità del linguaggio molto raramente, e quasi sempre solo con costanti o enumerazioni, mai con metodi. Il compromesso, per me, non vale quasi mai la pena.


9
Concordato. Ho usato occasionalmente importatori statici di veeery in cui hanno reso il codice significativamente più facile da seguire.
Neil Coffey,

65

Un altro uso ragionevole per le importazioni statiche è con JUnit 4. Nelle versioni precedenti di JUnit i metodi piacevano assertEqualsed failerano ereditati dall'estensione della classe di test junit.framework.TestCase.

// old way
import junit.framework.TestCase;

public class MyTestClass extends TestCase {
    public void myMethodTest() {
        assertEquals("foo", "bar");
    }
}

In JUnit 4, le classi di test non devono più essere estese TestCasee possono invece utilizzare le annotazioni. È quindi possibile importare staticamente i metodi di asserzione da org.junit.Assert:

// new way
import static org.junit.Assert.assertEquals;

public class MyTestClass {
    @Test public void myMethodTest() {
        assertEquals("foo", "bar");
        // instead of
        Assert.assertEquals("foo", "bar");
    }
}

Documenti JUnit che lo usano in questo modo.


4
Sono d'accordo. La semplificazione dei casi di test è un luogo in cui è improbabile che l'intento venga frainteso.
Bill Michell,

6
Abbiamo avuto questo nel nostro progetto e in realtà abbiamo avuto problemi con le persone che usano assert () e pensano erroneamente che provenga dalla loro importazione statica del pacchetto Assert. Una volta riscontrato questo problema, una rapida scansione della nostra base di codice ha rilevato circa 30 istanze di questo nei nostri test, il che significa che 30 asserzioni NON erano in esecuzione quando il framework di test è stato eseguito perché il flag DEBUG non è impostato quando eseguiamo i test.
Chris Williams,

27

L'efficace Java, seconda edizione , alla fine dell'articolo 19, nota che è possibile utilizzare le importazioni statiche se ci si trova pesantemente usando costanti di una classe di utilità. Penso che questo principio si applicherebbe alle importazioni statiche di costanti e metodi.

import static com.example.UtilityClassWithFrequentlyUsedMethods.myMethod;

public class MyClass {
    public void doSomething() {
        int foo= UtilityClassWithFrequentlyUsedMethods.myMethod();
        // can be written less verbosely as
        int bar = myMethod();
    }
}

Questo ha vantaggi e svantaggi. Rende il codice un po 'più leggibile a spese della perdita di alcune informazioni immediate su dove viene definito il metodo. Tuttavia, un buon IDE ti lascerà andare alla definizione, quindi questo non è un grosso problema.

Dovresti comunque usarlo con parsimonia, e solo se ti ritrovi a usare le cose dal file importato molte, molte volte.

Modifica: aggiornato per essere più specifico ai metodi, poiché è a questo che si riferisce questa domanda. Il principio si applica indipendentemente da ciò che viene importato (costanti o metodi).


1
La mia domanda riguarda i metodi di importazione statica , non i campi.
Miserabile variabile

7
Forse UtilityClassWithFrequentlyUsedMethodsdeve essere abbreviato.
Steve Kuo,


@ Rob-Hruska Non potrei semplicemente avvolgere un metodo o un campo di importazione statica in un nuovo metodo o campo se ho intenzione di usarli frequentemente? Ciò mi consentirebbe di non importare staticamente? come: double myPI = Math.PI;e quindi posso solo continuare a fare riferimento a myPIanziché Math.PI.
Abdul,

@Abdul - Sì, potresti farlo.
Rob Hruska,

14

Sono d'accordo che possono essere problematici dal punto di vista della leggibilità e dovrebbero essere usati con parsimonia. Ma quando si utilizza un metodo statico comune possono effettivamente aumentare la leggibilità. Ad esempio, in una classe di test JUnit, metodi come assertEqualssono evidenti da dove provengono. Allo stesso modo per i metodi da java.lang.Math.


5
E cosa c'è di così brutto nel vedere Math.round (d) contro round (d)?
Steve Kuo,

5
@SteveKuo - per lo stesso motivo per cui i matematici usano nomi di variabili di una lettera quando manipolano le formule: ci sono momenti in cui nomi più lunghi interferiscono con la leggibilità dell'istruzione generale. Considera una formula che coinvolge più funzioni trigonometriche. Una formula matematica facilmente comprensibile: sin x cos y + cos x sin y. In Java diventa: Math.sin(x) * Math.cos(y) + Math.cos(x) * Math.sin(y). Orribile da leggere.
ToolmakerSteve

@ToolmakerSteve, ecco perché mi mancava usingcosì tanto la direttiva in C ++: possono essere locali .
Franklin Yu,

11

Penso che l'importazione statica sia davvero utile per rimuovere nomi di classi ridondanti quando si usano classi utils come ArrayseAssertions .

Non so perché, ma Ross ha ignorato l'ultima frase che lo menziona nella documentazione a cui fa riferimento .

Se usato in modo appropriato, l'importazione statica può rendere il tuo programma più leggibile, rimuovendo il bollettino di ripetizione dei nomi delle classi.

Fondamentalmente copiato da questo blog: https://medium.com/alphadev-thoughts/static-imports-are-great-but-underused-e805ba9b279f

Quindi per esempio:

Asserzioni nei test

Questo è il caso più ovvio su cui tutti siamo d'accordo

Assertions.assertThat(1).isEqualTo(2);

// Use static import instead
assertThat(1).isEqualTo(2);

Utilità lezioni ed enumerazioni

Il nome della classe può essere rimosso in molti casi quando si usano le classi utils per facilitare la lettura del codice

List<Integer> numbers = Arrays.asList(1, 2, 3);

// asList method name is enough information
List<Integer> numbers = asList(1, 2, 3);

Il pacchetto java.time ha alcuni casi in cui dovrebbe essere usato

// Get next Friday from now, quite annoying to read
LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));

// More concise and easier to read
LocalDate.now().with(next(FRIDAY));

Esempio di quando non usare

// Ok this is an Optional
Optional.of("hello world");

// I have no idea what this is 
of("hello world");

10

Lo uso molto per Color.

static import java.awt.Color.*;

È molto improbabile che i colori vengano confusi con qualcos'altro.


1
Questo è uno dei migliori casi d'uso che ho visto che differisce dal vecchio JUnit / Hamcrest / TestNG.
kevinarpe,

3

Raccomando l'uso dell'importazione statica quando si utilizza OpenGL con Java, che è un caso d'uso che rientra nell'uso pesante di costanti da una classe di utilità categoria

Considera che

import static android.opengl.GLES20.*;

ti permette di portare il codice C originale e scrivere qualcosa di leggibile come:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(samplerUniform, 0);
glBindBuffer(GL_ARRAY_BUFFER, vtxBuffer);
glVertexAttribPointer(vtxAttrib, 3, GL_FLOAT, false, 0, 0);

invece di quella bruttezza diffusa comune:

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
GLES20.glUniform1i(samplerUniform, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vtxBuffer);
GLES20.glVertexAttribPointer(vtxAttrib, 3, GLES20.GL_FLOAT, false, 0, 0);

2

Le importazioni statiche sono l'unica "nuova" caratteristica di Java che non ho mai usato e che non intendo mai usare, a causa dei problemi appena menzionati.


Grazie Bombe. Bene, credo che abbiano più senso che debbano estendersi e interfacciarsi con solo un mucchio di finali statiche.
Miserabile variabile

2

Uso 'import static java.lang.Math. *' Quando eseguo il porting di codice pesante matematico da C / C ++ a java. I metodi matematici eseguono il mapping da 1 a 1 e semplificano la conversione del codice trasferito senza la qualifica del nome della classe.


2

Ho trovato questo molto utile quando si usano le classi di utilità.

Ad esempio, invece di utilizzare: if(CollectionUtils.isNotEmpty(col))

Posso invece:

import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
if(isNotEmpty(col))

Quale IMO aumenta la leggibilità del codice quando utilizzo questa utility più volte nel mio codice.


2

Parlando di unit test: la maggior parte delle persone usa le importazioni statiche per i vari metodi statici forniti dai framework di derisione , come when()o verify().

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

E, naturalmente, quando si utilizza l'unico e si afferma che si dovrebbe usare assertThat(), è utile importare staticamente i matcher hamcrest richiesti, come in:

import static org.hamcrest.Matchers.*;

1

Sono utili per ridurre la verbosità, in particolare nei casi in cui vengono chiamati molti metodi importati e la distinzione tra metodi locali e importati è chiara.

Un esempio: codice che coinvolge più riferimenti a java.lang.Math

Un altro: una classe builder XML in cui anteporre il nome classe a ogni riferimento nasconderebbe la struttura che si sta creando


1

Penso che le importazioni statiche siano ordinate per NLS in stile gettext.

import static mypackage.TranslatorUtil._;

//...
System.out.println(_("Hello world."));

Ciò contrassegna entrambi la stringa come una stringa che deve essere estratta e fornisce un modo semplice e pulito per sostituire la stringa con la sua traduzione.


1

L'importazione statica IMO è una caratteristica piuttosto interessante. È assolutamente vero che la forte dipendenza dall'importazione statica rende il codice illeggibile e difficile da capire a quale classe appartiene un metodo o un attributo statico. Tuttavia, nella mia esperienza, diventa una funzionalità utilizzabile soprattutto quando si progettano Utilclassi che forniscono alcuni metodi e attributi statici. L'ambiguità che si presenta quando si fornisce l'importazione statica può essere aggirata stabilendo standard di codice. Nella mia esperienza all'interno di un'azienda questo approccio è accettabile e rende il codice più pulito e di facile comprensione. Preferibilmente inserisco il . Apparentemente questo approccio viola gli standard di denominazione di Java ma fornisce chiarezza sul codice. Ad esempio, se abbiamo una classe AngleUtils:_ carattere in metodi statici anteriori e attributi statici (in qualche modo adottato da C)

public class AngleUtils {

    public static final float _ZERO = 0.0f;
    public static final float _PI   = 3.14f;

    public static float _angleDiff(float angle1, float angle2){

    }

    public static float _addAngle(float target, float dest){

    }
}

In questo caso l'importazione statica fornisce chiarezza e la struttura del codice mi sembra più elegante:

import static AngleUtils.*;

public class TestClass{

    public void testAngles(){

        float initialAngle = _ZERO;
        float angle1, angle2;
        _addAngle(angle1, angle2);
    }
}

Immediatamente qualcuno può dire a quale metodo o attributo proviene da un'importazione statica e nasconde le informazioni della classe a cui appartiene. Non suggerisco di usare l'importazione statica per le classi che sono parte integrante di un modulo e forniscono metodi statici e non statici poiché in questo caso è importante sapere quale classe fornisce determinate funzionalità statiche.


Grazie per il suggerimento di nominare. A proposito, un carattere di sottolineatura di fronte viene tradizionalmente usato in alcuni ambienti per nominare metodi / campi privati. Sto prendendo in considerazione una convenzione modificata, ad esempio H_per le importazioni da una Helperclasse di utilità che ho, o C_per Common, o U_per Utility. In alternativa, ho considerato l'utilizzo di uno o due nomi di classe di caratteri per queste classi ampiamente utilizzate, ma ero preoccupato che a volte potessero essere in conflitto con i nomi locali - avere un codice legacy con nomi di metodi maiuscoli.
ToolmakerSteve

-1

Devi usarli quando:

  • si desidera utilizzare switchun'istruzione con valori enum
  • desideri rendere il tuo codice difficile da capire

9
Questo non è vero. (1) È possibile utilizzare perfettamente le costanti di enum senza importarle staticamente. (2) Le importazioni statiche, per esempio, dei metodi della classe JUnit Assert sono chiare come una campana. "assertTrue (...)" è leggibile quanto "Assert.assertTrue (...)", forse moreso.
Alan Krueger,

5
se hai 5 importazioni statiche in una classe di 500 righe, è molto difficile dire da dove provengono i metodi.
davetron5000,

4
+1 per quando desideri rendere il tuo codice difficile da capire :)
Miserabile variabile

-5

Li uso quando posso. Ho IntelliJ configurato per ricordarmi se dimentico. Penso che appaia molto più pulito di un nome di pacchetto completo.


13
Stai pensando a importazioni regolari. Le importazioni statiche ti consentono di fare riferimento ai membri di una classe senza qualificarli con un nome di classe, ad es. Importazione statica java.lang.system.out; out.println ( "foo"); // invece di System.out.println ("pippo");
sk.

Ora questa è un'ottima spiegazione delle importazioni statiche ...
peccato che
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.