Warum ist die Verwendung eines Platzhalters mit einer Java-Importanweisung schlecht?

419

Es ist viel bequemer und sauberer, eine einzelne Aussage wie zu verwenden

import java.awt.*;

als eine Reihe von einzelnen Klassen zu importieren

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

Was ist falsch an der Verwendung eines Platzhalters in der importAnweisung?

jnancheta
quelle

Antworten:

518

Das einzige Problem dabei ist, dass es Ihren lokalen Namespace überfüllt. Nehmen wir zum Beispiel an, Sie schreiben eine Swing-App und benötigen diese java.awt.Eventund sind auch mit dem Kalendersystem des Unternehmens verbunden, das über eine solche verfügt com.mycompany.calendar.Event. Wenn Sie beide mithilfe der Platzhaltermethode importieren, geschieht Folgendes:

  1. Sie haben einen direkten Namenskonflikt zwischen java.awt.Eventund com.mycompany.calendar.Eventund können daher nicht einmal kompilieren.
  2. Sie können tatsächlich nur einen importieren (nur einer Ihrer beiden Importe .*), aber es ist der falsche, und Sie haben Schwierigkeiten herauszufinden, warum Ihr Code behauptet, der Typ sei falsch.
  3. Wenn Sie Ihren Code kompilieren, gibt es keine com.mycompany.calendar.Event, aber wenn sie später einen hinzufügen, wird Ihr zuvor gültiger Code plötzlich nicht mehr kompiliert.

Der Vorteil der expliziten Auflistung aller Importe besteht darin, dass ich auf einen Blick erkennen kann, welche Klasse Sie verwenden wollten, was das Lesen des Codes erheblich erleichtert. Wenn Sie nur eine kurze einmalige Sache machen, ist nichts explizit falsch , aber zukünftige Betreuer werden Ihnen für Ihre Klarheit danken.

Benjamin Pollack
quelle
7
Es ist das erste Szenario, das passieren wird. Der Compiler stellt fest, dass zwei Ereignisklassen vorhanden sind, und gibt einen Fehler aus.
jan.vdbergh
38
Überprüfen Sie unbedingt meinen Kommentar unten - es gibt ein größeres Problem mit Typen, die im Laufe der Zeit zu Bibliotheken von Drittanbietern hinzugefügt werden. Sie können Code kompilieren lassen, der die Kompilierung beendet, nachdem jemand einem Glas, von dem Sie abhängig sind, einen Typ hinzugefügt hat.
Scott Stanchfield
6
zu Problem 1: Technisch gesehen können Sie kompilieren, müssen jedoch jedes Mal den vollständig qualifizierten Klassennamen verwenden.
Kip
1
Sie können diese Art von Konflikten lösen, ohne jede Klasse explizit aufzulisten, was zu eigenen Problemen führt.
rpjohnst
196

Hier ist eine Abstimmung für Sternimporte. Eine import-Anweisung soll ein Paket importieren , keine Klasse. Es ist viel sauberer, ganze Pakete zu importieren. Die hier identifizierten Probleme (z. B. java.sql.Datevs java.util.Date) können leicht auf andere Weise behoben werden, werden durch bestimmte Importe nicht wirklich angegangen und rechtfertigen sicherlich nicht wahnsinnig pedantische Importe für alle Klassen. Es gibt nichts Beunruhigenderes, als eine Quelldatei zu öffnen und 100 Importanweisungen durchzublättern.

Durch bestimmte Importe wird das Refactoring schwieriger. Wenn Sie eine Klasse entfernen / umbenennen, müssen Sie alle spezifischen Importe entfernen . Wenn Sie eine Implementierung in eine andere Klasse im selben Paket wechseln, müssen Sie die Importe korrigieren. Während diese zusätzlichen Schritte automatisiert werden können, sind sie wirklich Produktivitätseinbußen ohne wirklichen Gewinn.

Selbst wenn Eclipse standardmäßig keine Klassenimporte durchführen würde, würden alle immer noch Sternimporte durchführen. Es tut mir leid, aber es gibt wirklich keine vernünftige Rechtfertigung für bestimmte Importe.

So gehen Sie mit Klassenkonflikten um:

import java.sql.*;
import java.util.*;
import java.sql.Date;
davetron5000
quelle
28
Genau. Obwohl ich nicht dagegen wäre, explizite Importe zu verwenden, bevorzuge ich immer noch Sternimporte. Sie betonen, dass die "Einheit der Wiederverwendung" das gesamte Paket ist, nicht seine einzelnen Typen. Die Gründe, die andere gegen Sternimporte aufgeführt haben, sind schwach, und meiner Erfahrung nach hat die Verwendung von Sternimporten nie zu tatsächlichen Schwierigkeiten geführt.
Rogério
32
Weitere Informationen dazu, warum es böse ist, finden Sie unter javadude.com/articles/importondemandisevil.html . Grundidee: Es kann dazu führen, dass Code nicht mehr kompiliert wird, wenn Klassen zu Paketen hinzugefügt werden, die Sie importieren (z. B. als List zu java.util hinzugefügt wurde ...)
Scott Stanchfield
61
Alle von Ihnen genannten Probleme können durch moderne IDEs gelöst werden (Ausblenden von Importen, Umgestalten des Klassennamens usw.).
Assylias
15
Ich sollte keine IDE verwenden müssen, um Quellcode zu lesen oder zu schreiben - Code sollte ohne spezielle Tools für sich allein lesbar sein, es sei denn, die Sprache ist unglaublich geisteskrank. In diesem Fall funktioniert Java hervorragend - verwenden Sie einfach Sternimporte. Es gibt keinen Grund, dies nicht zu tun.
davetron5000
42
@ davetron5000 Wenn Ihr Code mehr als 10 Platzhalterimporte enthält und Sie eine Klasse verwenden Foound wenn ich Ihren Code ohne Verwendung einer IDE lese (da Sie argumentieren, dass ich keine verwenden muss), woher weiß ich, von welchem ​​Paket er Foostammt? ? Sicher, mit einer IDE wird die IDE es mir sagen, aber Ihr gesamtes Argument ist, dass ich den Code ohne eine lesen kann. Durch explizite Importe wird der Code dokumentiert (ein guter Grund, Platzhalter zu vermeiden) , und es ist viel wahrscheinlicher, dass ich den Code ohne Verwendung einer IDE lese , als dass ich den Code ohne Verwendung einer IDE schreibe .
Andreas
169

Bitte lesen Sie meinen Artikel Import on Demand ist böse

Kurz gesagt, das größte Problem ist, dass Ihr Code beschädigt werden kann, wenn eine Klasse zu einem von Ihnen importierten Paket hinzugefügt wird . Zum Beispiel:

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

// ...

List list;

In Java 1.1 war dies in Ordnung; Die Liste wurde in java.awt gefunden und es gab keinen Konflikt.

Angenommen, Sie checken Ihren perfekt funktionierenden Code ein und ein Jahr später bringt ihn jemand anderes heraus, um ihn zu bearbeiten, und verwendet Java 1.2.

Java 1.2 hat java.util eine Schnittstelle namens List hinzugefügt. BOOM! Konflikt. Der perfekt funktionierende Code funktioniert nicht mehr.

Dies ist eine böse Sprachfunktion . Es gibt keinen Grund, warum der Code nicht mehr kompiliert werden sollte, nur weil einem Paket ein Typ hinzugefügt wurde ...

Außerdem ist es für einen Leser schwierig zu bestimmen, welches "Foo" Sie verwenden.

Scott Stanchfield
quelle
35
Dies ist keine gültige Entschuldigung. Wenn Sie die Java-Version ändern, erwarten Sie, dass einige Dinge fehlschlagen, genauso wie wenn Sie die Version einer Binärdatei ändern, die Ihr Code verwendet. In diesen Fällen würde der Code einen Kompilierungsfehler auslösen und es ist trivial zu beheben (siehe die vorherige Antwort: stackoverflow.com/a/149282/7595 )
Pablo Fernandez
36
@PabloFernandez - Nein - Wenn ich Code auschecke, der sich seit einem Jahr in einem Repository befindet, sollte er trotzdem kompiliert werden. Import-on-Demand kann leicht fehlschlagen, wenn neue Klassen vorhanden sind zu vorhandenen Paketen hinzugefügt werden, die ich importiert habe. Dies ist nicht nur ein Problem beim Aktualisieren von Java-Versionen. Außerdem - wenn eine API gut konzipiert ist, sollte sie beim Upgrade niemals vorhandenen Code beschädigen. Das einzige Mal, dass ich beim Aktualisieren von Java-Versionen den Code ändern musste, war der Import bei Bedarf und als Sun die XML-APIs in die Java-Laufzeit zog.
Scott Stanchfield
3
Das Hinzufügen einer Klasse (mit einem eindeutigen, vollständig qualifizierten Namen!) Zum Klassenpfad sollte keine Auswirkungen haben. Der Punkt hier ist, dass wenn Sie die Import-on-Demand-Syntax nicht verwenden, dies nicht der Fall ist . Verwenden Sie also nicht die schlechte Syntax, die die Sprache leider zulässt, und dies ist ein weniger reales Problem, mit dem Sie konfrontiert werden können.
Scott Stanchfield
28
Der Punkt meiner Antwort ist, dass es sich um eine unnötige Sprachfunktion handelt, die Probleme verursacht. Viele IDEs / Editoren übernehmen automatisch die Importerweiterung. Verwenden Sie vollqualifizierte Importe, und es besteht keine Möglichkeit, dass dieser bestimmte Fehler auftritt. Ich bin von diesem Problem getroffen worden, als ich unter dem Druck stand, einen Fehler in vorhandenem Code zu beheben, und Sie brauchen so etwas wirklich nicht, um von der eigentlichen Aufgabe abzulenken. java.util.Listvs java.awt.Listist nicht schlecht, um es herauszufinden, aber versuchen Sie es, wenn der Klassenname lautet Configurationund mehrere Abhängigkeitsbibliotheken ihn in ihrer neuesten Maven-Repo-Version hinzugefügt haben.
Scott Stanchfield
5
Wenn ich ein JAR aktualisiere, in dem die von mir verwendeten Klassen API-Forward-kompatibel sind, und keine Import-on-Demand-Syntax verwende, hat dies keinerlei Auswirkungen auf mich. Macht das für dich Sinn? Seien Sie nicht faul beim Definieren von Importen und dies ist kein Problem. Die Import-on-Demand-Syntax war ein Fehler bei der Definition der Java-Sprache. Eine vernünftige Sprache sollte solche Fehler nicht zulassen.
Scott Stanchfield
67

Es ist nicht schlecht, einen Platzhalter mit einer Java-Importanweisung zu verwenden.

In Clean Code empfiehlt Robert C. Martin tatsächlich, sie zu verwenden, um lange Importlisten zu vermeiden.

Hier ist die Empfehlung:

J1: Vermeiden Sie lange Importlisten, indem Sie Platzhalter verwenden

Wenn Sie zwei oder mehr Klassen aus einem Paket verwenden, importieren Sie das gesamte Paket mit

Paket importieren. *;

Lange Importlisten entmutigen den Leser. Wir wollen die Spitzen unserer Module nicht mit 80 Importzeilen überladen. Vielmehr möchten wir, dass die Importe eine präzise Aussage darüber sind, mit welchen Paketen wir zusammenarbeiten.

Bestimmte Importe sind harte Abhängigkeiten, Platzhalterimporte hingegen nicht. Wenn Sie speziell eine Klasse importieren, muss diese Klasse vorhanden sein. Wenn Sie jedoch ein Paket mit einem Platzhalter importieren, müssen keine bestimmten Klassen vorhanden sein. Die import-Anweisung fügt das Paket einfach dem Suchpfad hinzu, wenn nach Namen gesucht wird. Durch solche Importe entsteht also keine echte Abhängigkeit, und sie dienen daher dazu, unsere Module weniger gekoppelt zu halten.

Es gibt Zeiten, in denen die lange Liste bestimmter Importe nützlich sein kann. Wenn Sie beispielsweise mit Legacy-Code arbeiten und herausfinden möchten, für welche Klassen Sie Mocks und Stubs erstellen müssen, können Sie die Liste der spezifischen Importe durchgehen, um die tatsächlichen qualifizierten Namen aller dieser Klassen zu ermitteln und dann zu setzen die entsprechenden Stichleitungen vorhanden. Diese Verwendung für bestimmte Importe ist jedoch sehr selten. Darüber hinaus können Sie mit den meisten modernen IDEs die Wildcard-Importe mit einem einzigen Befehl in eine Liste spezifischer Importe konvertieren. Selbst im Legacy-Fall ist es besser, Platzhalter zu importieren.

Platzhalterimporte können manchmal zu Namenskonflikten und Mehrdeutigkeiten führen. Zwei Klassen mit demselben Namen, jedoch in unterschiedlichen Paketen, müssen speziell importiert oder zumindest speziell qualifiziert werden, wenn sie verwendet werden. Dies kann ein Ärgernis sein, ist jedoch selten genug, dass die Verwendung von Platzhalterimporten im Allgemeinen immer noch besser ist als bestimmte Importe.

hwiechers
quelle
41
Ich würde Robert C. Martin vorschlagen, bessere Muster zu verwenden, um präzisere Pakete und eigene Klassen zu erstellen, für die keine 80 Importzeilen erforderlich sind. Die vielen Klassen, die für den Import innerhalb einer einzelnen Klasse benötigt werden, betteln nur um "Entropie, Entropie, bitte brechen Sie mich ..." und weisen auf den Grund hin, den Import zu vermeiden, wie in Scott Stanchfields Antworten
Ray
28
So sehr ich allgemein liebe, was Onkel Bob zu sagen hat, muss ich ihm auch in diesem Fall nicht zustimmen.
33
Lange Importlisten entmutigen den Leser. - Diese Behauptung hat eine ungültige Vermutung. Programmierer müssen den Quellcode nicht von oben nach unten lesen. Wir können Importlisten überhaupt nicht lesen. Wenn wir dies tun, lesen wir zur Verdeutlichung möglicherweise nur einen der Importe. In anderen Fällen können die Importe vollständig reduziert werden, wenn wir in einer IDE arbeiten. Unabhängig von der Quelle ist dies heute ein schlechter Rat.
Andy Thomas
10
Nur um ein gewisses Gegengewicht zu schaffen, wenn es darum geht, Behörden zu diesem Thema zu zitieren: Der Google Java Style Guide sowie der Java Style Guide von Twitter (der weitgehend auf dem Google basiert, um fair zu sein) verbieten ausdrücklich Wildcard-Importe. Sie liefern jedoch keine Begründung für diese Entscheidung.
Anothernode
1
Wahrscheinlich der einzige Punkt, dem ich in Clean Code nicht zustimmte. Es muss durch einige Zeilen mit Importanweisungen scrollen oder sich bemühen, herauszufinden, woher die Klasse kommt. Ich ziehe es vor, leicht zu identifizieren, woher eine bestimmte Klasse kommt.
Ganesh Satpute
27

Performance : Keine Auswirkung auf die Leistung, da der Bytecode gleich ist. Dies führt jedoch zu einigen Kompilierungskosten.

Kompilierung : Auf meinem PC dauert das Kompilieren einer leeren Klasse ohne Importieren 100 ms, beim Importieren von Java jedoch dieselbe Klasse. * 170 ms.

Vinish Nain
quelle
3
import java.*importiert nichts. Warum sollte es einen Unterschied machen?
Marquis von Lorne
6
Es macht einen Unterschied, weil es während der Kompilierung durchsucht wird.
LegendLength
25

Es überfüllt Ihren Namespace und erfordert, dass Sie alle mehrdeutigen Klassennamen vollständig angeben. Das häufigste Auftreten ist:

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

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

Es hilft auch dabei, Ihre Abhängigkeiten konkret zu machen, da alle Ihre Abhängigkeiten oben in der Datei aufgeführt sind.

Hazzen
quelle
20
  1. Es hilft, Klassennamenkonflikte zu identifizieren: zwei Klassen in verschiedenen Paketen mit demselben Namen. Dies kann mit dem * Import maskiert werden.
  2. Abhängigkeiten werden explizit angegeben, sodass jeder, der Ihren Code später lesen muss, weiß, was Sie importieren wollten und was Sie nicht importieren wollten.
  3. Dies kann die Kompilierung beschleunigen, da der Compiler nicht das gesamte Paket durchsuchen muss, um Abhängigkeiten zu identifizieren, obwohl dies bei modernen Compilern normalerweise keine große Sache ist.
  4. Die unbequemen Aspekte expliziter Importe werden mit modernen IDEs minimiert. Mit den meisten IDEs können Sie den Importabschnitt reduzieren, damit er nicht im Weg ist, Importe bei Bedarf automatisch füllen und nicht verwendete Importe automatisch identifizieren, um sie zu bereinigen.

Die meisten Orte, an denen ich gearbeitet habe und die eine signifikante Menge von Java verwenden, machen explizite Importe zu einem Teil des Codierungsstandards. Manchmal verwende ich * immer noch für das schnelle Prototyping und erweitere dann die Importlisten (einige IDEs erledigen dies auch für Sie), wenn ich den Code produziere.

Josh Segall
quelle
Ich mag die meisten Ihrer Punkte, aber es war speziell Nummer 4, die mich dazu brachte, Ihre Antwort zu bewerten. Moderne IDEs entfernen die meisten Argumente gegen die Verwendung expliziter Importe ...
Sheldon R.
Möglicherweise liegt ein Teil des Problems darin, wie die Standard-Java-Bibliotheken mit vielen Klassen innerhalb desselben Pakets angeordnet sind. Im Gegensatz zur Anwendung eines „Single-Responsibility-Prinzips“ auf ein Paket.
LegendLength
11

Ich bevorzuge bestimmte Importe, da ich so alle in der Datei verwendeten externen Referenzen anzeigen kann, ohne die gesamte Datei zu betrachten. (Ja, ich weiß, dass nicht unbedingt vollständig qualifizierte Referenzen angezeigt werden. Aber ich vermeide sie, wann immer dies möglich ist.)

Jeff C.
quelle
9

In einem früheren Projekt stellte ich fest, dass der Wechsel von * -Importen zu bestimmten Importen die Kompilierungszeit um die Hälfte reduzierte (von ungefähr 10 Minuten auf ungefähr 5 Minuten). Mit dem * -Import durchsucht der Compiler jedes der aufgelisteten Pakete nach einer Klasse, die der von Ihnen verwendeten entspricht. Diese Zeit kann zwar klein sein, summiert sich jedoch für große Projekte.

Ein Nebeneffekt des * -Imports war, dass Entwickler gängige Importzeilen kopieren und einfügen, anstatt darüber nachzudenken, was sie benötigen.

Michael Hall
quelle
11
Muss eine Menge Importlinien oder ein wirklich erbärmliches Entwicklungssystem gewesen sein, damit dies wahr ist. Ich verwende import- * und kann meine gesamte Codebasis von 2107 Klassen in weniger als 2 Minuten kompilieren .
Lawrence Dol
6

Im DDD-Buch

Suchen Sie in jeder Entwicklungstechnologie, auf der die Implementierung basiert, nach Möglichkeiten, die Arbeit beim Refactoring von MODULES zu minimieren. In Java gibt es keinen Ausweg aus dem Import in einzelne Klassen, aber Sie können mindestens ganze Pakete gleichzeitig importieren, was die Absicht widerspiegelt, dass Pakete sehr zusammenhängende Einheiten sind, während gleichzeitig der Aufwand für das Ändern von Paketnamen verringert wird.

Und wenn es den lokalen Namespace überfüllt, ist es nicht Ihre Schuld - beschuldigen Sie die Größe des Pakets.

Ivan
quelle
3
  • Es gibt keine Auswirkungen auf die Laufzeit, da der Compiler das * automatisch durch konkrete Klassennamen ersetzt. Wenn Sie die .class-Datei dekompilieren, werden Sie nie sehen import ...*.

  • C # verwendet immer * (implizit), da Sie nur den usingPaketnamen verwenden können. Sie können den Klassennamen niemals angeben. Java führt die Funktion nach c # ein. (Java ist in vielerlei Hinsicht so schwierig, aber es geht über dieses Thema hinaus).

  • Wenn Sie in Intellij Idea "Importe organisieren", werden automatisch mehrere Importe desselben Pakets durch * ersetzt. Dies ist eine obligatorische Funktion, da Sie sie nicht deaktivieren können (obwohl Sie den Schwellenwert erhöhen können).

  • Der in der akzeptierten Antwort aufgeführte Fall ist ungültig. Ohne * hast du immer noch das gleiche Problem. Sie müssen den Paketnamen in Ihrem Code angeben, unabhängig davon, ob Sie * verwenden oder nicht.

Leon
quelle
1
In IntelliJ ist dies keine obligatorische Funktion und kann deaktiviert werden.
Bastien7
3

Für den Datensatz: Wenn Sie einen Import hinzufügen, geben Sie auch Ihre Abhängigkeiten an.

Sie können schnell erkennen, welche Abhängigkeiten Dateien haben (ausgenommen Klassen mit demselben Namespace).

Magallane
quelle
Zustimmen. Der Motivator ist nicht so viel Leistung oder Zusammenstellung, sondern die menschliche Lesbarkeit Ihres Codes. Stellen Sie sich vor, Sie lesen Code ohne IDE - zum Beispiel auf GitHub. Das plötzliche Nachschlagen aller Referenzen, die nicht in der Datei definiert sind, die Sie gerade lesen, wird nervenaufreibend langweilig.
Leo Orientis
2

Das Wichtigste ist, dass der Import java.awt.*Ihr Programm mit einer zukünftigen Java-Version inkompatibel machen kann:

Angenommen, Sie haben eine Klasse mit dem Namen "ABC", verwenden JDK 8 und importieren java.util.*. Nehmen wir nun an, dass Java 9 herauskommt und eine neue Klasse im Paket hat java.util, die zufällig auch "ABC" heißt. Ihr Programm wird jetzt nicht unter Java 9 kompiliert, da der Compiler nicht weiß, ob Sie mit dem Namen "ABC" Ihre eigene Klasse oder die neue Klasse in meinenjava.awt .

Sie werden dieses Problem nicht haben, wenn Sie nur die Klassen explizit importieren java.awt, die Sie tatsächlich verwenden.

Ressourcen:

Java-Importe

Affy
quelle
3
Tipp: Sie könnten Streamals Beispiel für eine neue Klasse in Java in java.util in Java 8 hinzugefügt haben ...
Clint Eastwood
2

Unter all den gültigen Punkten, die auf beiden Seiten gemacht wurden, habe ich meinen Hauptgrund, den Platzhalter zu vermeiden, nicht gefunden: Ich möchte den Code lesen und direkt wissen können, was jede Klasse ist oder ob ihre Definition nicht in der Sprache oder ist die Datei, wo es zu finden ist. Wenn mehr als ein Paket mit * importiert wird, muss ich jedes einzelne durchsuchen, um eine Klasse zu finden, die ich nicht erkenne. Die Lesbarkeit ist oberstes Gebot, und ich stimme zu, dass für den Code des Codes keine IDE erforderlich sein sollte .

user109439
quelle
Wenn Sie dies zu seiner vollständigen logischen Schlussfolgerung bringen, sollte Ihr Stil darin bestehen, überhaupt keine Importe zu verwenden und anstelle von "new LinkedList" immer "new java.util.LinkedList" zu verwenden und dies überall konsequent zu tun .
Erwin Smout