Perché usare una wild card con una dichiarazione di importazione Java non è buona?

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

import java.awt.*; 

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 jolly nella dichiarazione di import ?

L’unico problema è che ingombra il tuo spazio dei nomi locale. Ad esempio, supponiamo che tu stia scrivendo un’app Swing e quindi hai bisogno di java.awt.Event e stai anche com.mycompany.calendar.Event con il sistema di calendario della società, che ha com.mycompany.calendar.Event . Se importi entrambi utilizzando il metodo jolly, si verifica una di queste tre cose:

  1. Hai un conflitto di denominazione a com.mycompany.calendar.Event tra java.awt.Event e com.mycompany.calendar.Event e quindi non puoi nemmeno compilarlo.
  2. In realtà riesci a importarne solo uno (solo una delle due importazioni lo fa .* ), Ma è quello sbagliato, e ti sforzi di capire perché il tuo codice sostiene che il tipo sia sbagliato.
  3. Quando si compila il codice non esiste com.mycompany.calendar.Event , ma quando successivamente ne aggiungi uno il codice precedentemente valido smette improvvisamente di essere compilato.

Il vantaggio di elencare esplicitamente tutte le importazioni è che posso capire a colpo d’occhio quale class intendevi usare, il che semplifica la lettura del codice. Se stai solo facendo una cosa rapida, non c’è nulla di esplicitamente sbagliato , ma i futuri manutentori ti ringrazieranno per la tua chiarezza altrimenti.

Ecco un voto per le importazioni di stelle. Una dichiarazione di importazione ha lo scopo di importare un pacchetto , non una class. È molto più pulito importare interi pacchetti; i problemi identificati qui (es. java.sql.Date vs java.util.Date ) sono facilmente risolvibili con altri mezzi, non realmente indirizzati da importazioni specifiche e certamente non giustificano folle importazioni pedanti su tutte le classi. Non c’è nulla di più sconcertante dell’apertura di un file sorgente e della necessità di sfogliare 100 istruzioni di importazione.

Fare importazioni specifiche rende più difficile il refactoring; se rimuovi / rinomina una class, devi rimuovere tutte le sue importazioni specifiche. Se si passa un’implementazione a una class diversa nello stesso pacchetto, è necessario correggere le importazioni. Mentre questi passaggi aggiuntivi possono essere automatizzati, sono davvero colpi di produttività senza alcun guadagno reale.

Se Eclipse non eseguisse importazioni di class per impostazione predefinita, tutti continuerebbero a eseguire importazioni di stelle. Mi dispiace, ma non c’è alcuna giustificazione razionale per fare importazioni specifiche.

Ecco come affrontare i conflitti di class:

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

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 class viene aggiunta a un pacchetto che importi. Per esempio:

 import java.awt.*; import java.util.*; // ... List list; 

In Java 1.1, questo andava bene; Elenco è stato trovato in java.awt e non c’è stato alcun conflitto.

Ora supponiamo che tu controlli il tuo codice perfettamente funzionante, e un anno dopo qualcun altro lo elabora per modificarlo, e stia usando Java 1.2.

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

Questa è una funzione di linguaggio EVIL . Non c’è alcun motivo per cui il codice dovrebbe smettere di compilare solo perché un tipo viene aggiunto a un pacchetto …

Inoltre, rende difficile per un lettore determinare quale “Pippo” stai usando.

Non è male usare una wild card con una dichiarazione di importazione Java.

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

Ecco la raccomandazione:

J1: Evita elenchi di importazione lunghi utilizzando i caratteri jolly

Se si utilizzano due o più classi da un pacchetto, quindi 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 importazioni. Preferiamo piuttosto che le importazioni siano una dichiarazione concisa su quali pacchetti collaboriamo.

Le importazioni specifiche sono difficili da gestire, mentre le importazioni con caratteri jolly non lo sono. Se si importa in modo specifico una class, allora quella class 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 quando cerca i nomi. Quindi nessuna vera dipendenza viene creata da tali importazioni e pertanto servono a mantenere i nostri moduli meno accoppiati.

Ci sono momentjs in cui la lunga lista di importazioni specifiche può essere utile. Ad esempio, se hai a che fare con il codice legacy e vuoi scoprire di quali classi hai bisogno per creare mock e stub, puoi andare a capo dell’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, gli IDE più moderni ti consentiranno di convertire le importazioni con caratteri jolly in un elenco di importazioni specifiche con un singolo comando. Quindi, anche nel caso precedente è meglio importare i caratteri jolly.

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

Riordina il tuo spazio dei nomi, richiedendoti di specificare tutti i nomi di class 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, in quanto tutte le tue dipendenze sono elencate nella parte superiore del file.

Prestazioni : nessun impatto sulle prestazioni in quanto il codice byte è lo stesso. anche se ciò comporterà delle spese generali di compilazione.

Compilazione : sulla mia macchina personale, compilare una class vuota senza importare qualcosa richiede 100 ms ma stessa class quando import java. * Richiede 170 ms.

  1. Aiuta a identificare i conflitti tra nomi: 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 tuo codice in seguito sappia cosa intendi importare e cosa non intendi importare.
  3. Può rendere una compilazione più veloce perché il compilatore non deve cercare l’intero pacchetto per identificare le dipendenze, anche se questo di solito non è un grosso problema con i moderni compilatori.
  4. Gli aspetti scomodi delle importazioni esplicite sono ridotti al minimo con gli IDE moderni. La maggior parte degli IDE consente di comprimere la sezione di importazione in modo che non sia d’intralcio, di popolare automaticamente le importazioni quando necessario e di identificare automaticamente le importazioni inutilizzate per aiutarle a ripulirle.

La maggior parte dei posti in cui ho lavorato che usano una quantità significativa di Java rendono le importazioni esplicite parte dello standard di codifica. A volte utilizzo ancora * per la prototipazione rapida e quindi espandi le liste di importazione (alcuni IDE faranno questo anche per te) quando produci il codice.

Preferisco le importazioni specifiche, perché mi permette di vedere tutti i riferimenti esterni usati nel file senza guardare l’intero file. (Sì, so che non mostrerà necessariamente riferimenti completi, ma li evito ogni volta che è ansible).

In un progetto precedente ho scoperto che il passaggio da * -import a importazioni specifiche riduceva i tempi di compilazione della metà (da circa 10 minuti a circa 5 minuti). L’* -import fa in modo che il compilatore cerchi tutti i pacchetti elencati per una class corrispondente a quella che hai usato. Mentre questa volta può essere piccola, si aggiunge a grandi progetti.

Un lato dell’affetto * era che gli sviluppatori avrebbero copiato e incollato le linee di importazione comuni piuttosto che pensare a ciò di cui avevano bisogno.

Nel libro DDD

In qualsiasi tecnologia di sviluppo su cui si baserà l’implementazione, cercare modi per ridurre al minimo il lavoro di refactoring dei MODULI. In Java, non vi è alcuna fuga dall’importazione in singole classi, ma è ansible 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 ingombrare lo spazio dei nomi locale non è colpa tua, biasima la dimensione del pacchetto.

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

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

Non avrai questo problema quando importerai solo quelle classi esplicitamente da java.awt che usi effettivamente.

risorse:

Importazioni Java

Tra tutti i punti validi fatti su entrambi i lati non ho trovato il motivo principale per evitare il jolly: mi piace poter leggere il codice e sapere direttamente cosa è ogni class, o se la sua definizione non è nella lingua o il file, dove trovarlo. Se viene importato più di un pacchetto con * Devo cercare ognuno di essi per trovare una class che non riconosco. La leggibilità è suprema, e sono d’accordo che il codice non dovrebbe richiedere un IDE per leggerlo.

  • Non vi è alcun impatto sul runtime, in quanto il compilatore sostituisce automaticamente * con nomi di classi concreti. Se decompilate il file .class, non vedreste mai import ...* .

  • C # usa sempre * (implicitamente) poiché puoi using solo il nome del pacchetto. Non puoi mai specificare il nome della class. Java introduce la funzione dopo c #. (Java è così complicato in molti aspetti ma è al di là di questo argomento).

  • In Idea Intellij quando si “organizza le importazioni”, sostituisce automaticamente più importazioni dello stesso pacchetto con *. Questa è una funzione di mandante in quanto non è ansible distriggersrla (sebbene sia ansible aumentare la soglia).

  • Il caso elencato dalla risposta accettata non è valido. Senza * hai ancora lo stesso problema. È necessario specificare il nome del pakage nel codice, indipendentemente dall’uso * o meno.