Perché l'utilizzo di un carattere jolly con un'istruzione di importazione Java non è corretto?


419

È molto più conveniente e più pulito usare una singola istruzione come

import java.awt.*;

piuttosto che importare un gruppo di singole classi

import java.awt.Panel;
import java.awt.Graphics;
import java.awt.Canvas;
...

Cosa c'è di sbagliato nell'usare un carattere jolly importnell'istruzione?

Risposte:


518

L'unico problema è che ingombra il tuo spazio dei nomi locale. Ad esempio, supponiamo che tu stia scrivendo un'app Swing e che quindi sia necessario java.awt.Evente che tu stia anche interfacciando con il sistema di calendario dell'azienda, che ha com.mycompany.calendar.Event. Se si importano entrambi utilizzando il metodo jolly, si verifica una di queste tre cose:

  1. Hai un vero e proprio conflitto di denominazione tra java.awt.Evente com.mycompany.calendar.Event, quindi non puoi nemmeno compilare.
  2. In realtà riesci solo a importarne una (solo una delle tue due importazioni lo fa .*), ma è quella sbagliata e fai fatica a capire perché il tuo codice afferma che il tipo è sbagliato.
  3. Quando compili il tuo codice non c'è com.mycompany.calendar.Event, ma quando successivamente ne aggiungono uno il tuo codice precedentemente valido interrompe improvvisamente la compilazione.

Il vantaggio di elencare esplicitamente tutte le importazioni è che posso dire a colpo d'occhio quale classe intendevi utilizzare, il che rende semplicemente molto più semplice la lettura del codice. Se stai semplicemente facendo una cosa una tantum, non c'è nulla di esplicitamente sbagliato , ma i futuri manutentori ti ringrazieranno per la tua chiarezza altrimenti.


7
È il primo scenario che accadrà. Il compilatore nota che esistono due classi di eventi e genera un errore.
Jan.vdbergh,

38
Assicurati di controllare il mio commento qui sotto: c'è un problema più grande con i tipi aggiunti alle librerie di terze parti nel tempo. Puoi avere un codice di compilazione che interrompe la compilazione dopo che qualcuno aggiunge un tipo a un vaso da cui dipendi.
Scott Stanchfield,

6
riguardo al problema 1: tecnicamente, puoi compilare, ma dovrai usare ogni volta il nome della classe pienamente qualificato.
Chiudi l'

1
È possibile risolvere questo tipo di conflitti senza elencare esplicitamente ogni classe, il che causa problemi propri.
rpjohnst,

196

Ecco un voto per le importazioni di stelle. Una dichiarazione di importazione ha lo scopo di importare un pacchetto , non una classe. È molto più pulito importare interi pacchetti; i problemi qui identificati (ad es. java.sql.Datevs java.util.Date) sono facilmente risolti con altri mezzi, non realmente affrontati da importazioni specifiche e certamente non giustificano importazioni follemente pedanti su tutte le classi. Non c'è nulla di più sconcertante che aprire un file sorgente e dover scorrere tra 100 istruzioni di importazione.

Effettuare importazioni specifiche rende più difficile il refactoring; se rimuovi / rinomina una classe, devi rimuovere tutte le sue importazioni specifiche. Se si passa un'implementazione a un'altra classe nello stesso pacchetto, è necessario correggere le importazioni. Mentre questi passaggi aggiuntivi possono essere automatizzati, sono in realtà un aumento della produttività senza alcun guadagno reale.

Anche se Eclipse non eseguisse l'importazione di classe per impostazione predefinita, tutti continuerebbero comunque a importare stelle. Mi dispiace, ma non esiste davvero alcuna giustificazione razionale per effettuare importazioni specifiche.

Ecco come affrontare i conflitti di classe:

import java.sql.*;
import java.util.*;
import java.sql.Date;

28
Sono d'accordo. Anche se non sarei contrario a utilizzare le importazioni esplicite, preferisco comunque utilizzare le importazioni da stelle. Sottolineano che l '"unità di riutilizzo" è l'intero pacchetto, non i suoi singoli tipi. I motivi per cui gli altri sono stati elencati contro le importazioni di stelle sono deboli e nella mia esperienza con l'importazione di stelle non è mai stata riscontrata alcuna reale difficoltà.
Rogério,

32
Vedi javadude.com/articles/importondemandisevil.html per i dettagli sul perché è malvagio. Idea di base: può causare l'interruzione della compilazione del codice quando le classi vengono aggiunte ai pacchetti importati (come quando Elenco è stato aggiunto a java.util ...)
Scott Stanchfield

61
Tutti i problemi citati possono essere risolti dai moderni IDE (nascondere le importazioni, il nome della classe di refactoring, ecc ...).
Assylias,

15
Non dovrei usare un IDE per leggere o scrivere il codice sorgente - il codice dovrebbe essere leggibile da solo senza strumenti speciali a meno che la lingua non sia incredibilmente braindead. In questo caso, Java funziona alla grande - basta usare le importazioni da stelle. Non c'è motivo di non farlo.
davetron5000,

42
@ davetron5000 Se il tuo codice contiene oltre 10 importazioni di caratteri jolly e usi la classe Foo, e se leggo il tuo codice senza usare un IDE (poiché il tuo argomento è che non dovrei usarne uno), come faccio a sapere da quale pacchetto Fooproviene ? Certo, usando un IDE, l'IDE me lo dirà, ma il tuo intero argomento è che dovrei essere in grado di leggere il codice senza uno. Fare importazioni esplicite aiuta a documentare il codice (ottimo motivo per evitare i caratteri jolly) , ed è molto più probabile che leggerò il codice senza usare un IDE, piuttosto che scriverò il codice senza usare un IDE.
Andreas,

169

per favore, vedi il mio articolo Import on Demand is Evil

In breve, il problema più grande è che il tuo codice può rompersi quando una classe viene aggiunta a un pacchetto importato. Per esempio:

import java.awt.*;
import java.util.*;

// ...

List list;

In Java 1.1, questo andava bene; L'elenco è stato trovato in java.awt e non si sono verificati conflitti.

Supponiamo ora di controllare il tuo codice perfettamente funzionante, e un anno dopo qualcun altro lo fa uscire per modificarlo e sta usando Java 1.2.

Java 1.2 ha aggiunto un'interfaccia denominata Elenco a java.util. BOOM! Conflitto. Il codice perfettamente funzionante non funziona più.

Questa è una funzione del linguaggio MALE . Non vi è alcun motivo per cui il codice dovrebbe interrompere la compilazione solo perché un tipo viene aggiunto a un pacchetto ...

Inoltre, rende difficile per un lettore determinare quale "Foo" stai usando.


35
Questa non è una scusa valida. Se stai cambiando la versione java, ti aspetti che alcune cose falliscano, la stessa cosa se cambi la versione di un binario che usa il tuo codice. In questi casi il codice genererebbe un errore di compilazione ed è banale da risolvere (vedi la risposta precedente: stackoverflow.com/a/149282/7595 )
Pablo Fernandez,

36
@PabloFernandez - No - Se controllo il codice che è stato in un repository per un anno, dovrebbe comunque essere compilato. L'importazione su richiesta può facilmente fallire quando vengono aggiunte nuove classi ai pacchetti esistenti che ho importato. Non è solo un problema durante l'aggiornamento delle versioni di Java. Inoltre, se un'API è progettata correttamente, non dovrebbe mai rompere il codice esistente durante l'aggiornamento. L'unica volta in cui ho dovuto modificare il codice durante l'aggiornamento delle versioni Java era a causa dell'importazione su richiesta e quando Sun estraeva le API XML nel runtime Java.
Scott Stanchfield,

3
L'aggiunta di una classe (con un nome univoco e completo!) Al percorso di classe non dovrebbe influire su nulla. Il punto qui è che se do_not non si usa la sintassi import-on-demand, non lo farà. Quindi non usare la sintassi sbagliata che la lingua purtroppo permette alla lingua e questo è un problema molto meno reale con cui puoi essere colpito.
Scott Stanchfield,

28
Il punto della mia risposta è che è una caratteristica linguistica non necessaria che causa problemi. Molti IDE / editor gestiscono automaticamente l'espansione delle importazioni. Utilizza importazioni qualificate e non è possibile che si verifichi questo particolare errore. Sono stato colpito da questo quando sono sotto pressione per correggere un bug nel codice esistente, e non hai davvero bisogno di qualcosa del genere come distrazione dal vero compito da svolgere. java.util.Listvs java.awt.Listnon è poi così male da capire, ma provalo quando il nome della classe è Configuratione più librerie di dipendenze l'hanno aggiunto nella loro ultima versione repo di maven.
Scott Stanchfield,

5
Se aggiorno un jar in cui le classi che utilizzo sono compatibili con l'API forward, e non utilizzo la sintassi import-on-demand, non mi influenzerà affatto. Ha senso per te? Non essere pigro nel definire le importazioni e questo non è un problema. La sintassi di importazione su richiesta è stata un errore nella definizione del linguaggio Java; un linguaggio ragionevole non dovrebbe consentire errori come questo.
Scott Stanchfield,

67

E ' non è male usare una wild card con una dichiarazione di importazione Java.

In Clean Code , Robert C. Martin in realtà consiglia di usarli per evitare lunghi elenchi di importazione.

Ecco la raccomandazione:

J1: evitare elenchi di importazione lunghi utilizzando i caratteri jolly

Se si utilizzano due o più classi da un pacchetto, importare l'intero pacchetto con

pacchetto di importazione. *;

Lunghi elenchi di importazioni sono scoraggianti per il lettore. Non vogliamo ingombrare le parti superiori dei nostri moduli con 80 linee di importazione. Piuttosto vogliamo che le importazioni siano una dichiarazione concisa su quali pacchetti collaboriamo.

Le importazioni specifiche sono forti dipendenze, mentre le importazioni con caratteri jolly non lo sono. Se si importa specificamente una classe, tale classe deve esistere. Ma se si importa un pacchetto con un carattere jolly, non è necessario che esistano classi particolari. L'istruzione import aggiunge semplicemente il pacchetto al percorso di ricerca durante la ricerca di nomi. Quindi nessuna vera dipendenza viene creata da tali importazioni e servono quindi a mantenere i nostri moduli meno accoppiati.

Ci sono momenti in cui il lungo elenco di importazioni specifiche può essere utile. Ad esempio, se hai a che fare con il codice legacy e vuoi scoprire per quali classi hai bisogno di creare mock e stub, puoi scorrere l'elenco delle importazioni specifiche per scoprire i veri nomi qualificati di tutte quelle classi e poi mettere gli stub appropriati in atto. Tuttavia, questo uso per importazioni specifiche è molto raro. Inoltre, la maggior parte degli IDE moderni ti consentirà di convertire le importazioni con caratteri jolly in un elenco di importazioni specifiche con un singolo comando. Quindi, anche nel caso legacy, è meglio importare i caratteri jolly.

Le importazioni di caratteri jolly possono talvolta causare conflitti di nomi e ambiguità. Due classi con lo stesso nome, ma in pacchetti diversi, dovranno essere importate in modo specifico, o almeno specificatamente qualificate quando utilizzate. Questo può essere un fastidio, ma è abbastanza raro che l'utilizzo delle importazioni con caratteri jolly sia generalmente migliore delle importazioni specifiche.


41
Suggerirei a Robert C. Martin di utilizzare modelli migliori per creare pacchetti e classi più concisi che non richiedono 80 linee di importazione. Il fatto che molte classi necessarie per l'importazione all'interno di una singola classe stiano solo supplicando "Entropia, Entropia, spezzami per favore ..." e indica il motivo per evitare l'importazione * descritta nelle risposte di Scott Stanchfields
Ray,

28
Per quanto generalmente ami ciò che lo zio Bob ha da dire, in questo caso devo anche essere in disaccordo con lui.

33
Lunghi elenchi di importazioni sono scoraggianti per il lettore. - Questa affermazione ha una presunzione non valida. I programmatori non sono tenuti a leggere il codice sorgente dall'alto verso il basso. Potremmo non leggere affatto le liste di importazione. Quando lo facciamo, potremmo leggere solo una delle importazioni, per chiarimenti. Altre volte, le importazioni potrebbero essere completamente collassate, se stiamo lavorando in un IDE. Indipendentemente dalla fonte, oggi questo è un cattivo consiglio.
Andy Thomas,

10
Giusto per fornire un contrappeso quando si tratta di citare le autorità su questo problema: la Guida allo stile Java di Google e la Guida allo stile Java di Twitter (che è in gran parte basata su quella di Google, per essere onesti) vietano specificamente le importazioni di caratteri jolly. Ma non forniscono alcuna motivazione per questa decisione.
nodo

1
Probabilmente l'unico punto in cui non ero d'accordo su Clean Code. Deve scorrere alcune righe di istruzioni di importazione o fatica a trovare da dove provenga la classe. Preferisco identificare facilmente la provenienza di una determinata classe.
Ganesh Satpute,

27

Prestazioni : nessun impatto sulle prestazioni poiché il codice byte è lo stesso. sebbene porterà ad alcune spese generali di compilazione.

Compilazione : sulla mia macchina personale, la compilazione di una classe vuota senza importare nulla richiede 100 ms ma la stessa classe quando importa java. * Richiede 170 ms.


3
import java.*non importa nulla. Perché dovrebbe fare la differenza?
Marchese di Lorne,

6
Fa la differenza perché viene cercato durante la compilazione.
LegendLength

25

Ingombra il tuo spazio dei nomi, richiedendo di specificare completamente tutti i nomi di classe che sono ambigui. L'evento più comune di questo è con:

import java.util.*;
import java.awt.*;

...
List blah; // Ambiguous, needs to be qualified.

Aiuta anche a rendere concrete le tue dipendenze, poiché tutte le dipendenze sono elencate nella parte superiore del file.


20
  1. Aiuta a identificare i conflitti di nomi di classe: due classi in pacchetti diversi che hanno lo stesso nome. Questo può essere mascherato con * import.
  2. Rende esplicite le dipendenze, in modo che chiunque debba leggere il codice in un secondo momento sappia cosa intendevi importare e cosa non intendevi importare.
  3. Può rendere più veloce la compilazione perché il compilatore non deve cercare l'intero pacchetto per identificare le depdenze, anche se di solito non è un grosso problema con i compilatori moderni.
  4. Gli aspetti scomodi delle importazioni esplicite sono ridotti al minimo con gli IDE moderni. La maggior parte degli IDE ti consente di comprimere la sezione di importazione in modo che non sia di ostacolo, popolare automaticamente le importazioni quando necessario e identifica automaticamente le importazioni inutilizzate per aiutarle a ripulirle.

La maggior parte dei posti in cui ho lavorato che utilizzano una quantità significativa di Java rendono le importazioni esplicite parte dello standard di codifica. A volte uso ancora * per la prototipazione rapida e quindi espando gli elenchi di importazione (alcuni IDE lo faranno anche per te) durante la produzione del codice.


Mi piacciono la maggior parte dei tuoi punti, ma è stato il numero 4 in particolare a farmi votare la tua risposta. Gli IDE moderni rimuovono la maggior parte degli argomenti contro l'uso di importazioni esplicite ...
Sheldon R.

Forse parte del problema qui è il modo in cui le librerie Java standard sono disposte con molte classi all'interno dello stesso pacchetto. Al contrario di applicare più di un "principio di sola responsabilità" a un pacchetto.
LegendLength

11

Preferisco importazioni specifiche, perché mi permette di vedere tutti i riferimenti esterni utilizzati nel file senza guardare l'intero file. (Sì, lo so che non mostrerà necessariamente riferimenti pienamente qualificati. Ma li evito quando possibile.)


9

In un precedente progetto ho scoperto che il passaggio da * -import a importazioni specifiche ha ridotto della metà il tempo di compilazione (da circa 10 minuti a circa 5 minuti). * -Import consente al compilatore di cercare ciascuno dei pacchetti elencati per una classe corrispondente a quella utilizzata. Mentre questa volta può essere piccolo, si aggiunge a grandi progetti.

Un effetto collaterale dell'importazione * era che gli sviluppatori avrebbero copiato e incollato le linee di importazione comuni anziché pensare a ciò di cui avevano bisogno.


11
Devono essere state molte linee di importazione o un sistema di sviluppo davvero patetico perché ciò sia vero. Uso import- * an Posso compilare la mia intera base di codice di 2107 classi in meno di 2 minuti.
Lawrence Dol

6

Nel libro DDD

In qualsiasi tecnologia di sviluppo su cui si baserà l'implementazione, cerca modi per ridurre al minimo il lavoro di refactoring MODULES. In Java, non c'è scampo dall'importazione in singole classi, ma puoi almeno importare interi pacchetti alla volta, riflettendo l'intenzione che i pacchetti siano unità altamente coesive riducendo allo stesso tempo lo sforzo di cambiare i nomi dei pacchetti.

E se ingombra lo spazio dei nomi locale non è colpa tua - incolpare la dimensione del pacchetto.


3
  • Non vi è alcun impatto sul runtime, poiché il compilatore sostituisce automaticamente * con nomi di classe concreti. Se decompilassi il file .class, non lo vedresti mai import ...*.

  • C # usa sempre * (implicitamente) poiché puoi solo il usingnome del pacchetto. Non puoi mai specificare il nome della classe. Java introduce la funzione dopo c #. (Java è così complicato sotto molti aspetti ma va oltre questo argomento).

  • In Intellij Idea quando "organizzi le importazioni", sostituisce automaticamente più importazioni dello stesso pacchetto con *. Questa è una funzione obbligatoria in quanto non è possibile disattivarla (sebbene sia possibile aumentare la soglia).

  • Il caso elencato dalla risposta accettata non è valido. Senza * hai ancora lo stesso problema. Devi specificare il nome della pakcage nel tuo codice, indipendentemente dal fatto che tu * usi o meno.


1
In IntelliJ, non è una funzione obbligatoria e può essere disattivata.
Bastien7,

3

Per la cronaca: quando aggiungi un'importazione, indichi anche le tue dipendenze.

Si potrebbe vedere rapidamente quali sono le dipendenze dei file (escluse le classi dello stesso spazio dei nomi).


Essere d'accordo. Il motivatore non è tanto la prestazione o la compilazione, ma la leggibilità umana del tuo codice. Immagina di leggere il codice senza un IDE, ad esempio su GitHub. Improvvisamente cercare ogni riferimento non definito nel file che stai leggendo diventa noiosamente noioso.
Leo Orientis,

2

Il più importante è che l'importazione java.awt.*può rendere il programma incompatibile con una futura versione di Java:

Supponiamo che tu abbia una classe chiamata "ABC", stai usando JDK 8 e importi java.util.*. Supponiamo ora che Java 9 esca e che abbia una nuova classe nel pacchetto java.utilche per coincidenza viene anche chiamata "ABC". Il tuo programma ora non verrà compilato su Java 9, perché il compilatore non sa se con il nome "ABC" intendi la tua classe o la nuova classe in java.awt.

Non avrai questo problema quando importi solo quelle classi esplicitamente da java.awtquelle che usi effettivamente.

risorse:

Importazioni Java


3
consiglio: avresti potuto usare Streamcome esempio di una nuova classe aggiunta in Java in java.util in Java 8 ...
Clint Eastwood

2

Tra tutti i punti validi fatti da entrambe le parti non ho trovato il motivo principale per evitare il carattere jolly: mi piace essere in grado di leggere il codice e sapere direttamente qual è ogni classe, o se la sua definizione non è nella lingua o il file, dove trovarlo. Se viene importato più di un pacchetto con *, devo cercare ciascuno di essi per trovare una classe che non riconosco. La leggibilità è suprema e sono d'accordo che il codice non dovrebbe richiedere un IDE per leggerlo.


Se lo porti alla sua completa conclusione logica, il tuo stile dovrebbe essere quello di non usare affatto le importazioni e invece di "new LinkedList" usa sempre "new java.util.LinkedList" e fallo costantemente ovunque .
Erwin Smout,
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.