De ce nu este bună utilizarea unui wild card cu o instrucțiune de import Java? (Programare, Java, Import, Wildcard)

jnancheta a intrebat.

Este mult mai convenabil și mai curat să folosiți o singură declarație precum

import java.awt.*;

decât să importați o grămadă de clase individuale

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

Ce este în neregulă cu utilizarea unui caracter sălbatic în instrucțiunea import în declarație?

17 răspunsuri
Benjamin Pollack

Singura problemă cu aceasta este că vă aglomerează spațiul de nume local. De exemplu, să presupunem că scrieți o aplicație Swing și, prin urmare, aveți nevoie de java.awt.Event, și, de asemenea, aveți o interfață cu sistemul de calendare al companiei, care are com.mycompany.calendar.Event. Dacă le importați pe amândouă folosind metoda wildcard, se întâmplă unul dintre aceste trei lucruri:

  1. Aveți un conflict de denumire între java.awt.Event și com.mycompany.calendar.Event, și astfel nu puteți nici măcar să compilați.
  2. De fapt, reușiți să importați doar unul (doar unul dintre cele două importuri face .*), dar este cel greșit și vă străduiți să vă dați seama de ce codul dumneavoastră pretinde că tipul este greșit.
  3. Atunci când compilați codul dvs. nu există niciun com.mycompany.calendar.Event, , dar când mai târziu se adaugă unul, codul dumneavoastră anterior valid încetează brusc să mai compileze.

Avantajul enumerării explicite a tuturor importurilor este că pot spune dintr-o privire ce clasă ați vrut să utilizați, ceea ce face ca citirea codului să fie mult mai ușoară. Dacă faceți doar un lucru rapid și punctual, nu există nimic care să fie explicit greșit, , dar, în caz contrar, viitorii mentenanți vă vor mulțumi pentru claritate.

Comentarii

  • Este primul scenariu care se va întâmpla. Compilatorul observă că există două clase Event și dă o eroare. –  > Por jan.vdbergh.
  • 51

  • Asigurați-vă că verificați comentariul meu de mai jos – există o problemă mai mare cu tipurile care sunt adăugate la librăriile terților de-a lungul timpului. Puteți avea cod de compilare care se oprește din compilare după ce cineva adaugă un tip la un jar de care depindeți. –  > Por Scott Stanchfield.
  • în ceea ce privește problema 1: din punct de vedere tehnic, puteți compila, dar va trebui să utilizați de fiecare dată numele complet calificat al clasei. –  > Por Kip.
  • Puteți rezolva acest tip de conflicte fără a lista fiecare clasă în mod explicit, ceea ce cauzează probleme de sine stătătoare. –  > Por rpjohnst.
  • Sunt surprins că acest răspuns a fost acceptat și are peste 500 de voturi. Literalmente, atunci când compilatoarele găsesc lucruri pentru tine, este un lucru bun, nu un lucru rău. Încă nu am văzut un argument despre importurile asterisk care să răspundă nevoilor zilnice de adevăr de bază ale dezvoltatorilor și care să nu fie o chestiune de autoritarism Checkstyle. –  > Por mobiusinversion.
davetron5000

Iată un vot pentru Star imports. O instrucțiune de import are rolul de a importa un pachet, și nu o clasă. Este mult mai curat să importați pachete întregi; problemele identificate aici (de ex. java.sql.Date vs java.util.Date) sunt ușor de remediat prin alte mijloace, nu prin într-adevăr abordate prin importuri specifice și, cu siguranță, nu justifică importuri nebunești și pedante pentru toate clasele. Nu există nimic mai deconcertant decât să deschizi un fișier sursă și să fii nevoit să parcurgi 100 de declarații de import.

Importurile specifice îngreunează refactorizarea; dacă eliminați/renumiți o clasă, trebuie să eliminați toate toate importurile sale specifice. Dacă treceți o implementare la o clasă diferită din același pachet, trebuie să reparați importurile. Deși acești pași suplimentari pot fi automatizați, ei reprezintă într-adevăr lovituri de productivitate pentru niciun câștig real.

Dacă Eclipse nu ar face importuri de clase specifice în mod implicit, toată lumea ar face în continuare importuri de stele. Îmi pare rău, dar chiar nu există nicio justificare rațională pentru a face importuri specifice.

Iată cum să vă ocupați de conflictele de clasă:

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

Comentarii

    32

  • Sunt de acord. Deși nu m-aș opune utilizării importurilor explicite, prefer totuși să folosesc importurile în stele. Acestea subliniază faptul că „unitatea de reutilizare” este întregul pachet, nu tipurile sale individuale. Motivele enumerate de alții împotriva importurilor în stea sunt slabe și, din experiența mea, utilizarea importurilor în stea nu a provocat niciodată dificultăți reale. –  > Por Rogério.
  • 35

  • A se vedea javadude.com/articles/importondemandiseisevil.html pentru detalii de ce este rău. Ideea de bază: poate determina oprirea compilării codului atunci când clasele sunt adăugate la pachetele pe care le importați (cum ar fi atunci când List a fost adăugat la java.util…).  > Por Scott Stanchfield.
  • 71

  • Toate problemele pe care le menționați pot fi rezolvate de IDE-urile moderne (ascunderea importurilor, refactorizarea numelui clasei, etc…). –  > Por assylias.
  • 16

  • Nu ar trebui să fiu nevoit să folosesc un IDE pentru a citi sau a scrie codul sursă – codul ar trebui să poată fi citit de unul singur, fără instrumente speciale, cu excepția cazului în care limbajul este incredibil de deșteptător. În acest caz, Java funcționează foarte bine – trebuie doar să folosiți importuri în stea. Nu există niciun motiv pentru a nu o face. –  > Por davetron5000.
  • 50

  • @davetron5000 Dacă codul dvs. conține 10+ importuri wildcard, și folosiți clasa Foo, , și dacă vă citesc codul fără să folosesc un IDE (din moment ce argumentul dvs. este că nu ar trebui să folosesc unul), cum voi ști ce pachet Foo a provenit? Sigur, dacă folosesc un IDE, IDE-ul îmi va spune, dar întregul dvs. argument este că ar trebui să pot să să citesc codul fără un astfel de instrument. Importurile explicite ajută documentarea codului (un motiv important pentru a evita caracterele sălbatice), , și este mult mai probabil ca eu să fiu să citesc codul fără a utiliza un IDE, decât că voi fi scriu codul fără a utiliza un IDE. –  > Por Andreas.
Scott Stanchfield

vă rog să consultați articolul meu Importul la cerere este rău

Pe scurt, cea mai mare problemă este că codul dvs. se poate întrerupe atunci când o clasă este adăugată la un pachet pe care îl importați. De exemplu:

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

// ...

List list;

În Java 1.1, acest lucru era în regulă; List se găsea în java.awt și nu exista niciun conflict.

Acum, să presupunem că vă verificați codul perfect funcțional și, un an mai târziu, altcineva îl scoate pentru a-l edita și folosește Java 1.2.

Java 1.2 a adăugat o interfață numită List la java.util. BOOM! Conflict. Codul perfect funcțional nu mai funcționează.

Acesta este un EVIL caracteristică a limbajului. Nu există NU motiv pentru care codul să nu se mai compileze doar pentru că un tip este adăugat la un pachet…

În plus, este dificil pentru un cititor să determine ce „Foo” folosiți.

Comentarii

    38

  • Aceasta nu este o scuză valabilă. Dacă schimbi versiunea java, te aștepți cumva ca unele lucruri să eșueze, același lucru dacă schimbi versiunea unui binar pe care îl folosește codul tău. În aceste cazuri, codul ar arunca o eroare de compilare și este banal de rezolvat (vezi răspunsul anterior: stackoverflow.com/a/149282/7595) –  > Por Pablo Fernandez.
  • 37

  • @PabloFernandez – Nu – Dacă verific un cod care se află într-un depozit de un an, ar trebui să se compileze în continuare. Importul la cerere poate eșua cu ușurință atunci când clasele noi sunt adăugate la pachetele existente pe care le-am importat. Nu este o problemă doar atunci când se actualizează versiunile Java. De asemenea, dacă o API este bine concepută, ar trebui să să nu fie niciodată să strice codul existent la actualizare. Singura dată când a trebuit să schimb codul la actualizarea versiunilor Java a fost din cauza importului la cerere și când Sun a introdus API-urile XML în timpul de execuție Java. –  > Por Scott Stanchfield.
  • A ADĂUGAREA unei clase (cu un nume unic, complet calificat!) în classpath nu ar trebui să afecteze nimic. Ideea este că, dacă do_not folosiți sintaxa de import la cerere, nu se va întâmpla acest lucru. Așadar, nu folosiți sintaxa proastă pe care limbajul o permite, din păcate, și aceasta este o problemă mai puțin foarte reală cu care vă puteți lovi. –  > Por Scott Stanchfield.
  • 29

  • Ideea răspunsului meu este că este o caracteristică inutilă a limbajului care cauzează probleme. Multe IDE-uri/editoare se ocupă automat de extinderea importului. Folosiți importuri complet calificate și nu există nicio șansă ca această eroare să apară. Am fost lovit de aceasta atunci când am fost presat să repar o eroare în codul existent și chiar nu aveți nevoie de așa ceva ca o distragere a atenției de la sarcina reală. java.util.List vs java.awt.List nu este prea greu de înțeles, dar încercați să o faceți atunci când numele clasei este Configuration și mai multe biblioteci de dependență au adăugat-o în cea mai recentă versiune maven repo. –  > Por Scott Stanchfield.
  • Dacă actualizez un borcan în care clasele pe care le folosesc sunt compatibile cu API-forward, ȘI nu folosesc sintaxa import-on-demand, nu mă va afecta deloc. Are sens pentru dumneavoastră? Nu fiți leneși în definirea importurilor și aceasta nu este o problemă. Sintaxa „import-on-demand” a fost o greșeală în definirea limbajului Java; un limbaj rezonabil nu ar trebui să permită erori de acest gen. –  > Por Scott Stanchfield.
hwiechers

Este not rău să folosiți un wild card cu o instrucțiune de import Java.

În Clean Code, Robert C. Martin chiar recomandă utilizarea lor pentru a evita listele lungi de importuri.

Iată care este recomandarea:

J1: Evitați listele lungi de importuri prin utilizarea de caractere joker

Dacă folosiți două sau mai multe clase dintr-un pachet, importați întregul pachet cu

import package.*;

Listele lungi de importuri sunt descurajante pentru cititor. Nu dorim să aglomerăm partea superioară a modulelor noastre cu 80 de linii de importuri. Mai degrabă dorim ca importurile să fie o declarație concisă despre pachetele cu care colaborăm.

Importurile specifice sunt dependențe stricte, în timp ce importurile wildcard nu sunt. Dacă importați în mod specific o clasă, atunci clasa respectivă trebuie să existe. Dar dacă importați un pachet cu un wildcard, nu este necesar să existe clase specifice. Instrucțiunea de import adaugă pur și simplu pachetul la calea de căutare atunci când se caută nume. Astfel, astfel de importuri nu creează o dependență reală și, prin urmare, ele servesc la menținerea modulelor noastre mai puțin cuplate.

Există momente în care lista lungă de importuri specifice poate fi utilă. De exemplu, dacă aveți de-a face cu un cod moștenit și doriți să aflați pentru ce clase trebuie să construiți mocks și stubs, puteți parcurge lista de importuri specifice pentru a afla adevăratele nume calificate ale tuturor acelor clase și apoi să puneți stubs-urile corespunzătoare. Cu toate acestea, această utilizare a importurilor specifice este foarte rară. Mai mult, majoritatea IDE-urilor moderne vă permit să convertiți importurile cu caractere wildcarded într-o listă de importuri specifice printr-o singură comandă. Așadar, chiar și în cazul moștenit este mai bine să importați wildcards.

Importurile cu caractere wildcard pot cauza uneori conflicte de nume și ambiguități. Două clase cu același nume, dar în pachete diferite, vor trebui să fie importate în mod specific sau cel puțin calificate în mod specific atunci când sunt utilizate. Acest lucru poate fi o pacoste, dar este suficient de rar pentru ca utilizarea importurilor wildcard să fie în general mai bună decât importurile specifice.

Comentarii

    46

  • I-aș sugera lui Robert C. Martin să folosească modele mai bune pentru a crea pachete și clase proprii mai concise care să nu necesite 80 de linii de importuri. Atât de multe clase necesare pentru import în interiorul unei singure clase este doar o cerșeală „Entropy, Entropy, break me please…” și subliniază motivul pentru a evita importul * subliniat în răspunsurile lui Scott Stanchfields…  > Por Ray.
  • 29

  • Oricât de mult îmi place, în general, ce are de spus unchiul Bob, în acest caz trebuie să nu fiu de acord cu el. – user4229245
  • 36

  • Listele lungi de importuri sunt descurajante pentru cititor. — Această afirmație are o prezumție invalidă. Programatorii nu sunt obligați să citească codul sursă de sus în jos. Este posibil să nu citim deloc listele de importuri. Atunci când o facem, s-ar putea să citim doar unul dintre importuri, pentru clarificare. Alteori, dacă lucrăm într-un IDE, este posibil ca importurile să nu fie incluse în întregime. Indiferent de sursă, în prezent, acesta este un sfat prost. –  > Por Andy Thomas.
  • Doar pentru a oferi o oarecare contrapondere atunci când vine vorba de citarea autorităților în această problemă: The Ghidul de stil Google Java precum și Ghidul de stil Java al Twitter (care se bazează în mare parte pe cel de la Google, ca să fim corecți) interzic în mod specific importurile cu wildcard. Dar nu oferă nicio justificare pentru această decizie. –  > Por anothernode.
  • Probabil singurul punct cu care nu am fost de acord în Clean Code. Este să trebuiască să parcurgi câteva rânduri de declarații de import sau să te chinui să găsești de unde vine clasa. Prefer să pot identifica cu ușurință de unde provine o anumită clasă. –  > Por Ganesh Satpute.
Vinish Nain

Performanță: Nu există niciun impact asupra performanței, deoarece codul de octeți este același.Deși va duce la unele costuri suplimentare de compilare.

Compilare: pe mașina mea personală, compilarea unei clase goale fără a importa nimic durează 100 ms, dar aceeași clasă, atunci când se importă java.*, durează 170 ms.

Comentarii

  • import java.* nu importă nimic. De ce ar fi o diferență? –  > Por utilizator207421.
  • Face o diferență pentru că este căutat în timpul compilării. –  > Por LegendLength.
  • Am impresia că această comparație nu este congruentă cu întrebarea, deoarece compară nimic cu un import wildcard. Aș fi curios care este diferența de timp de compilare atunci când importați o clasă prin wildcard vs. în mod specific. Și din moment ce compilatorul „caută” în pachet un wildcard, bănuiesc că diferența de timp variază în funcție de mărimea pachetului și de câte clase din același pachet sunt importate. –  > Por kugo2006.
hazzen

Vă aglomerează spațiul de nume, cerându-vă să specificați complet orice nume de clasă care este ambiguu. Cea mai frecventă apariție a acestei situații este cu:

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

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

De asemenea, ajută la concretizarea dependențelor, deoarece toate dependențele sunt listate în partea de sus a fișierului.

Josh Segall
  1. Ajută la identificarea conflictelor de nume de clasă: două clase din pachete diferite care au același nume. Acest lucru poate fi mascat cu * import.
  2. Face ca dependențele să fie explicite, astfel încât oricine trebuie să vă citească mai târziu codul să știe ce ați vrut să importați și ce nu ați vrut să importați.
  3. Poate face ca unele compilări să fie mai rapide, deoarece compilatorul nu trebuie să caute în întregul pachet pentru a identifica dependențele, deși, de obicei, acest lucru nu reprezintă o problemă majoră în cazul compilatoarelor moderne.
  4. Aspectele neplăcute ale importurilor explicite sunt reduse la minimum cu IDE-urile moderne. Majoritatea IDE-urilor vă permit să reduceți secțiunea de import pentru a nu vă încurca, să completați automat importurile atunci când este necesar și să identificați automat importurile neutilizate pentru a le curăța.

Majoritatea locurilor în care am lucrat și care folosesc o cantitate semnificativă de Java fac ca importurile explicite să facă parte din standardul de codare. Uneori încă mai folosesc * pentru prototipuri rapide și apoi extind listele de importuri (unele IDE-uri vor face acest lucru și pentru dumneavoastră) atunci când produc codul.

Comentarii

  • Îmi plac majoritatea punctelor dvs., dar a fost în mod special punctul 4 care m-a determinat să vă votez răspunsul. IDE-urile moderne elimină majoritatea argumentelor împotriva utilizării importurilor explicite… –  > Por Sheldon R..
  • Poate că o parte a problemei aici este modul în care sunt dispuse bibliotecile standard java, cu multe clase în cadrul aceluiași pachet. Spre deosebire de aplicarea mai degrabă a unui „principiu de responsabilitate unică” la un pachet. –  > Por LegendLength.
Jeff C

Prefer importurile specifice, deoarece îmi permite să văd toate referințele externe utilizate în fișier fără să mă uit la întregul fișier. (Da, știu că nu va arăta neapărat referințele complet calificate. Dar eu le evit ori de câte ori este posibil).

Michael Hall

Într-un proiect anterior am constatat că trecerea de la *-importuri la importuri specifice a redus timpul de compilare la jumătate (de la aproximativ 10 minute la aproximativ 5 minute). *-importul face ca compilatorul să caute în fiecare dintre pachetele listate o clasă care să se potrivească cu cea utilizată. În timp ce acest timp poate fi mic, el se adaugă pentru proiectele mari.

Un efect secundar al *-importului a fost acela că dezvoltatorii copiau și lipeau liniile comune de import în loc să se gândească la ceea ce aveau nevoie.

Comentarii

  • Trebuie să fi fost mult de linii de import sau un foarte jalnic sistem de dezvoltare pentru ca acest lucru să fie adevărat. Eu folosesc import-* și îmi pot compila întreaga bază de cod de 2107 clase în mai puțin de 2 minute. –  > Por Lawrence Dol.
Ivan

În cartea DDD

În orice tehnologie de dezvoltare pe care se va baza implementarea, căutați modalități de minimizare a muncii de refactorizare MODULE . În Java, nu există nici o scăpare de la importul în clase individuale, dar puteți cel puțin importa pachete întregi la un moment dat, reflectând intenția ca pachetele să fie unități foarte coezive, reducând în același timp efortul de a schimba numele pachetelor.

Iar dacă aglomerează spațiul de nume local, nu este vina dumneavoastră – dați vina pe dimensiunea pachetului.

Leon
  • Nu există niciun impact în timpul execuției, deoarece compilatorul înlocuiește automat * cu nume de clase concrete. Dacă ați decompila fișierul .class, nu veți vedea niciodată import ...*.

  • C# întotdeauna utilizează * (implicit), deoarece puteți doar using numele pachetului. Nu puteți specifica niciodată numele clasei. Java introduce această caracteristică după c#. (Java este atât de complicat în multe aspecte, dar depășește acest subiect).

  • În Intellij Idea, atunci când faceți „organize imports”, înlocuiește automat importurile multiple ale aceluiași pachet cu *. Aceasta este o caracteristică obligatorie, deoarece nu o puteți dezactiva (deși puteți mări pragul).

  • Cazul enumerat de răspunsul acceptat nu este valabil. Fără * aveți în continuare aceeași problemă. Trebuie să specificați numele pachetului în codul dumneavoastră, indiferent dacă utilizați sau nu *.

Comentarii

  • În IntelliJ, nu este o caracteristică obligatorie și poate fi dezactivată. –  > Por Bastien7.
Affy

Cea mai importantă este că importul java.awt.* poate face ca programul dvs. să fie incompatibil cu o versiune Java viitoare:

Să presupunem că aveți o clasă numită „ABC”, că utilizați JDK 8 și că importați java.util.*. Acum, să presupunem că apare Java 9, care are o nouă clasă în pachetul java.util care, din întâmplare, se întâmplă să se numească tot „ABC”. Programul dvs. nu se va compila acum pe Java 9, deoarece compilatorul nu știe dacă prin numele „ABC” vă referiți la clasa dvs. sau la noua clasă din pachet. java.awt.

Nu veți avea această problemă dacă importați doar clasele care provin în mod explicit din java.awt pe care le utilizați efectiv.

Resurse:

Importuri Java

Comentarii

  • sfat: ați fi putut folosi Stream ca exemplu de clasă nouă adăugată în Java în java.util în Java 8… –  > Por Clint Eastwood.
user109439

Printre toate punctele valide prezentate de ambele părți nu am găsit motivul principal pentru a evita wildcard-ul: Îmi place să pot citi codul și să știu direct ce este fiecare clasă, sau dacă definiția ei nu se află în limbaj sau în fișier, unde să o găsesc. Dacă mai multe pachete sunt importate cu *, trebuie să le caut în fiecare dintre ele pentru a găsi o clasă pe care nu o recunosc. Lizibilitatea este supremă și sunt de acord că codul nu ar trebui să fie să necesite un IDE pentru a-l citi.

Comentarii

  • Dacă duceți acest lucru la concluzia sa logică, atunci stilul dvs. ar trebui să fie acela de a nu folosi deloc importuri și, în loc de „new LinkedList”, să folosiți întotdeauna „new java.util.LinkedList” și să faceți acest lucru în mod consecvent. peste tot. –  > Por Erwin Smout.
magallanes

Pentru înregistrare:Când adăugați un import, indicați și dependențele.

Ați putea vedea rapid care sunt dependențele fișierelor (cu excepția claselor din același spațiu de nume).

Comentarii

  • Sunt de acord. Motivația nu este atât de mult performanța sau compilarea, ci lizibilitatea umană a codului tău. Imaginați-vă că citiți codul fără un IDE – pe GitHub, de exemplu. Dintr-o dată, căutarea fiecărei referințe care nu este definită în fișierul pe care îl citiți devine plictisitor de plictisitoare. –  > Por Leo Orientis.
Milan Paudyal

Iată câteva lucruri pe care le-am găsit în legătură cu acest subiect.

  • În timpul compilării, compilatorul încearcă să găsească clasele care sunt utilizate în cod din importul .*, iar codul de octeți corespunzător va fi generat prin selectarea claselor utilizate din importul .*. Astfel, codul de octeți va fi același în cazul utilizării importului .* sau a importului de nume de clase, iar performanța în timpul execuției va fi, de asemenea, aceeași datorită aceluiași cod de octeți.

  • La fiecare compilare, compilatorul trebuie să analizeze toate clasele din pachetul .* pentru a se potrivi cu clasele care sunt utilizate efectiv în cod. Prin urmare, codul cu importuri .* necesită mai mult timp în timpul procesului de compilare în comparație cu importurile cu nume de clasă .class.

  • Utilizarea importului .* contribuie la un cod mai curat.

  • Utilizarea importului .* poate crea ambiguitate atunci când folosim două clase cu același nume din două pachete diferite. De exemplu, Date este disponibil în ambele pachete.

      import java.util.*;
      import java.sql.*;
    
      public class DateDemo {
          private Date utilDate;
          private Date sqlDate;
      }
    

Leo Orientis

Uitați de spațiile de nume aglomerate… Și gândiți-vă la bietul suflet care trebuie să vă citească și să vă înțeleagă codul pe GitHub, în vi, Notepad++ sau alt editor de text non-IDE.

Acea persoană trebuie să caute minuțios fiecare simbol care provine dintr-unul dintre wildcards în toate clasele și referințele din fiecare domeniu wildcardat… doar pentru a înțelege ce naiba se întâmplă.

Dacă scrieți cod doar pentru compilator – și știți ce faceți – sunt sigur că nu există nicio problemă cu wildcards.

Dar dacă alți oameni – inclusiv viitorul dumneavoastră – vor să înțeleagă rapid un anumit fișier de cod la o singură citire, atunci referințele explicite ajută foarte mult.

Testilla

Importul tuturor claselor dintr-un pachet este considerat o abordare oarbă. Un motiv important pentru acest lucru este că aglomerează spațiul de nume al claselor și ar putea duce la conflicte între clase din pachete diferite cu același nume.

Popularea specifică a claselor necesare evită această problemă și arată în mod clar ce versiuni au fost dorite. Acest lucru este bun pentru menținerea codului.