Pourquoi l'utilisation d'un caractère générique avec une déclaration d'importation Java est-elle mauvaise?

419

Il est beaucoup plus pratique et plus propre d'utiliser une seule déclaration comme

import java.awt.*;

que d'importer un tas de classes individuelles

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

Quel est le problème avec l'utilisation d'un caractère générique dans la importdéclaration?

jnancheta
la source

Réponses:

518

Le seul problème est qu'il encombre votre espace de noms local. Par exemple, disons que vous écrivez une application Swing, et donc que vous en avez besoin java.awt.Event, et que vous vous connectez également au système de calendrier de l'entreprise, qui l'a com.mycompany.calendar.Event. Si vous importez les deux à l'aide de la méthode générique, l'une des trois choses suivantes se produit:

  1. Vous avez un conflit de dénomination entre java.awt.Eventet com.mycompany.calendar.Eventet vous ne pouvez donc même pas compiler.
  2. En fait, vous ne parvenez à en importer qu'une seule (une seule de vos deux importations le fait .*), mais ce n'est pas la bonne, et vous avez du mal à comprendre pourquoi votre code prétend que le type est incorrect.
  3. Lorsque vous compilez votre code, il n'y en a pas com.mycompany.calendar.Event, mais lorsqu'ils en ajoutent plus tard, votre code précédemment valide cesse soudainement de compiler.

L'avantage de répertorier explicitement toutes les importations est que je peux dire en un coup d'œil quelle classe vous vouliez utiliser, ce qui facilite simplement la lecture du code. Si vous faites juste une chose ponctuelle rapide, il n'y a rien de mal explicitement , mais les futurs responsables vous remercieront pour votre clarté sinon.

Benjamin Pollack
la source
7
C'est le premier scénario qui se produira. Le compilateur remarque qu'il existe deux classes d'événements et donne une erreur.
jan.vdbergh
38
Assurez-vous de vérifier mon commentaire ci-dessous - il y a un plus gros problème avec les types ajoutés aux bibliothèques tierces au fil du temps. Vous pouvez avoir un code de compilation qui arrête la compilation après que quelqu'un a ajouté un type à un fichier dont vous dépendez.
Scott Stanchfield
6
concernant le problème 1: techniquement, vous pouvez compiler, mais vous devrez utiliser le nom de classe complet à chaque fois.
Kip
1
Vous pouvez résoudre ce type de conflits sans répertorier explicitement chaque classe, ce qui entraîne des problèmes qui lui sont propres.
rpjohnst
196

Voici un vote pour les importations d'étoiles. Une instruction import est destinée à importer un package , pas une classe. Il est beaucoup plus propre d'importer des packages entiers; les problèmes identifiés ici (par exemple java.sql.Datevs java.util.Date) sont facilement résolus par d'autres moyens, pas vraiment traités par des importations spécifiques et ne justifient certainement pas les importations incroyablement pédantes sur toutes les classes. Il n'y a rien de plus déconcertant que d'ouvrir un fichier source et d'avoir à parcourir 100 instructions d'importation.

Faire des importations spécifiques rend la refactorisation plus difficile; si vous supprimez / renommez une classe, vous devez supprimer toutes ses importations spécifiques. Si vous basculez une implémentation vers une classe différente dans le même package, vous devez aller corriger les importations. Bien que ces étapes supplémentaires puissent être automatisées, ce sont vraiment des gains de productivité sans gain réel.

Même si Eclipse ne faisait pas d'importations de classe par défaut, tout le monde ferait toujours des importations d'étoiles. Je suis désolé, mais il n'y a vraiment aucune justification rationnelle pour effectuer des importations spécifiques.

Voici comment gérer les conflits de classe:

import java.sql.*;
import java.util.*;
import java.sql.Date;
davetron5000
la source
28
Je suis d'accord. Bien que je ne sois pas opposé à l'utilisation d'importations explicites, je préfère toujours utiliser les importations en étoile. Ils soulignent que "l'unité de réutilisation" est l'ensemble du paquet, pas ses types individuels. Les raisons énumérées par d'autres contre les importations d'étoiles sont faibles et, d'après mon expérience, l'utilisation d'importations d'étoiles n'a jamais causé de difficultés réelles.
Rogério
32
Voir javadude.com/articles/importondemandisevil.html pour savoir pourquoi c'est mal. Idée de base: cela peut entraîner l'arrêt de la compilation du code lorsque des classes sont ajoutées aux packages que vous importez (comme lorsque List a été ajouté à java.util ...)
Scott Stanchfield
61
Tous les problèmes que vous mentionnez peuvent être résolus par des IDE modernes (masquage des importations, refactoring du nom de classe, etc ...).
assylias
15
Je ne devrais pas avoir à utiliser un IDE pour lire ou écrire du code source - le code devrait être lisible seul sans outils spéciaux à moins que la langue ne soit incroyablement braindead. Dans ce cas, Java fonctionne très bien - utilisez simplement les importations d'étoiles. Il n'y a aucune raison de ne pas le faire.
davetron5000
42
@ davetron5000 Si votre code contient plus de 10 importations de caractères génériques et que vous utilisez la classe Foo, et si je lis votre code sans utiliser un IDE (puisque votre argument est que je ne devrais pas avoir à en utiliser un), comment saurai-je de quel paquet Fooprovient ? Bien sûr, en utilisant un IDE, l'IDE me le dira, mais tout votre argument est que je devrais être capable de lire le code sans un. Les importations explicites aident à documenter le code (une excellente raison d'éviter les caractères génériques) , et il est beaucoup plus probable que je lise le code sans utiliser un IDE, que que j'écris le code sans utiliser un IDE.
Andreas
169

s'il vous plaît voir mon article Import on Demand is Evil

En bref, le plus gros problème est que votre code peut se casser lorsqu'une classe est ajoutée à un package que vous importez. Par exemple:

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

// ...

List list;

En Java 1.1, c'était bien; La liste a été trouvée dans java.awt et il n'y a pas eu de conflit.

Supposons maintenant que vous archiviez votre code parfaitement fonctionnel et qu'un an plus tard, quelqu'un d'autre le fasse sortir pour l'éditer et utilise Java 1.2.

Java 1.2 a ajouté une interface nommée List à java.util. BOOM! Conflit. Le code fonctionnant parfaitement ne fonctionne plus.

Il s'agit d'une fonction de langage EVIL . Il n'y a AUCUNE raison pour que le code arrête de compiler juste parce qu'un type est ajouté à un package ...

De plus, il est difficile pour un lecteur de déterminer quel "Foo" vous utilisez.

Scott Stanchfield
la source
35
Ce n'est pas une excuse valable. Si vous changez la version java, vous vous attendez à ce que certaines choses échouent, même chose si vous changez la version d'un binaire que votre code utilise. Dans ces cas, le code lancerait une erreur de compilation et il est trivial de corriger (voir la réponse précédente: stackoverflow.com/a/149282/7595 )
Pablo Fernandez
36
@PabloFernandez - Nope - Si je vérifie le code qui est dans un référentiel depuis un an, il devrait toujours être compilé. L'importation à la demande peut facilement échouer lorsque de nouvelles classes sont ajoutées aux packages existants que j'ai importés. Ce n'est pas seulement un problème lors de la mise à niveau des versions Java. De plus, si une API est bien conçue, elle ne doit jamais casser le code existant lors de la mise à niveau. La seule fois où j'ai eu besoin de changer de code lors de la mise à niveau des versions java était à cause de l'importation à la demande et lorsque Sun a introduit les API XML dans le runtime java.
Scott Stanchfield
3
AJOUTER une classe (avec un nom unique et complet!) Au chemin de classe ne devrait rien affecter. Le point ici est que si vous n'utilisez pas la syntaxe d' importation à la demande, ce ne sera pas le cas. Donc, n'utilisez pas la mauvaise syntaxe que le langage permet malheureusement et c'est un problème de moins en moins réel que vous pouvez rencontrer.
Scott Stanchfield
28
Le point de ma réponse est que c'est une fonctionnalité de langage inutile qui cause des problèmes. De nombreux IDE / éditeurs gèrent automatiquement l'expansion des importations. Utilisez des importations entièrement qualifiées et il n'y a aucune chance que cette erreur se produise. J'ai été frappé par celui-ci lorsque je suis sous pression pour corriger un bogue dans le code existant, et vous n'avez vraiment pas besoin de quelque chose comme ça pour vous distraire de la vraie tâche à accomplir. java.util.Listvs java.awt.Listn'est pas trop mal à comprendre, mais essayez-le lorsque le nom de la classe est Configurationet que plusieurs bibliothèques de dépendances l'ont ajouté dans leur dernière version de référentiel maven.
Scott Stanchfield
5
Si je mets à jour un bocal où les classes que j'utilise sont compatibles API-forward, ET que je n'utilise pas la syntaxe d'importation à la demande, cela ne m'affectera pas du tout. Cela vous semble-t-il logique? Ne soyez pas paresseux sur la définition des importations et ce n'est pas un problème. La syntaxe d'importation à la demande était une erreur dans la définition du langage Java; un langage raisonnable ne devrait pas permettre de telles erreurs.
Scott Stanchfield
67

Il n'est pas mauvais d'utiliser un caractère générique avec une instruction d'importation Java.

Dans Clean Code , Robert C. Martin recommande en fait de les utiliser pour éviter les longues listes d'importation.

Voici la recommandation:

J1: éviter les longues listes d'importation en utilisant des caractères génériques

Si vous utilisez deux classes ou plus à partir d'un package, importez le package complet avec

package d'importation. *;

Les longues listes d'importations sont intimidantes pour le lecteur. Nous ne voulons pas encombrer les sommets de nos modules avec 80 lignes d'importations. Nous voulons plutôt que les importations soient une déclaration concise sur les packages avec lesquels nous collaborons.

Les importations spécifiques sont des dépendances matérielles, contrairement aux importations génériques. Si vous importez spécifiquement une classe, cette classe doit exister. Mais si vous importez un package avec un caractère générique, aucune classe particulière n'a besoin d'exister. L'instruction import ajoute simplement le package au chemin de recherche lors de la recherche de noms. Donc, aucune véritable dépendance n'est créée par de telles importations, et elles servent donc à garder nos modules moins couplés.

Il y a des moments où la longue liste d'importations spécifiques peut être utile. Par exemple, si vous traitez du code hérité et que vous voulez savoir pour quelles classes vous avez besoin de construire des mocks et des stubs, vous pouvez parcourir la liste des importations spécifiques pour trouver les vrais noms qualifiés de toutes ces classes, puis mettre les talons appropriés en place. Cependant, cette utilisation pour des importations spécifiques est très rare. De plus, la plupart des IDE modernes vous permettront de convertir les importations génériques en une liste d'importations spécifiques avec une seule commande. Ainsi, même dans le cas hérité, il est préférable d'importer des caractères génériques.

Les importations de caractères génériques peuvent parfois provoquer des conflits de noms et des ambiguïtés. Deux classes portant le même nom, mais dans des packages différents, devront être spécifiquement importées, ou au moins spécifiquement qualifiées lors de leur utilisation. Cela peut être gênant mais est suffisamment rare pour que l'utilisation d'importations génériques soit généralement meilleure que les importations spécifiques.

hwiechers
la source
41
Je suggère à Robert C. Martin d'utiliser de meilleurs modèles pour créer des packages et des classes plus concis qui ne nécessitent pas 80 lignes d'importations. Le fait que de nombreuses classes soient nécessaires à l'importation dans une seule classe ne fait que mendier `` Entropie, entropie, cassez-moi s'il vous plaît ... '' et indique la raison pour éviter les importations * décrites dans les réponses de Scott Stanchfields
Ray
28
Autant que j'aime généralement ce que l'oncle Bob a à dire, dans ce cas, je dois également être en désaccord avec lui.
33
Les longues listes d'importations sont intimidantes pour le lecteur. - Cette affirmation a une présomption invalide. Les programmeurs ne sont pas tenus de lire le code source de haut en bas. Il est possible que nous ne lisions pas du tout les listes d'importation. Lorsque nous le faisons, nous pourrions lire une seule des importations, pour plus de clarté. À d'autres moments, les importations peuvent être entièrement réduites, si nous travaillons dans un IDE. Quelle que soit la source, c'est aujourd'hui un mauvais conseil.
Andy Thomas
10
Juste pour fournir un contrepoids lorsqu'il s'agit de citer les autorités sur cette question: le Google Java Style Guide ainsi que le Java Style Guide de Twitter (qui est largement basé sur celui de Google, pour être juste) interdisent spécifiquement les importations de caractères génériques. Mais ils ne fournissent aucune justification à cette décision.
anothernode
1
Probablement le seul point que je n'étais pas d'accord dans Clean Code. Il faut faire défiler quelques lignes d'instructions d'importation ou avoir du mal à trouver d'où vient la classe. Je préfère identifier facilement d'où vient une certaine classe.
Ganesh Satpute
27

Performances : aucun impact sur les performances car le code d'octet est identique. même si cela entraînera des frais généraux de compilation.

Compilation : sur ma machine personnelle, Compiler une classe vide sans rien importer prend 100 ms mais la même classe lors de l'importation java. * Prend 170 ms.

Vinish Nain
la source
3
import java.*n'importe rien. Pourquoi cela ferait-il une différence?
Marquis de Lorne
6
Cela fait une différence car il est recherché lors de la compilation.
LegendLength
25

Il encombre votre espace de noms, vous obligeant à spécifier complètement tous les noms de classe qui sont ambigus. L'occurrence la plus courante de ceci est avec:

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

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

Cela permet également de concrétiser vos dépendances, car toutes vos dépendances sont répertoriées en haut du fichier.

hazzen
la source
20
  1. Il aide à identifier les conflits de nom de classe: deux classes dans des packages différents qui ont le même nom. Cela peut être masqué avec l'importation *.
  2. Il rend les dépendances explicites, afin que quiconque doit lire votre code plus tard sache ce que vous vouliez importer et ce que vous ne vouliez pas importer.
  3. Cela peut rendre la compilation plus rapide car le compilateur n'a pas à rechercher l'ensemble du package pour identifier les dépendances, bien que ce ne soit généralement pas un problème énorme avec les compilateurs modernes.
  4. Les aspects gênants des importations explicites sont minimisés avec les IDE modernes. La plupart des IDE vous permettent de réduire la section d'importation afin qu'elle ne gêne pas, de remplir automatiquement les importations en cas de besoin et d'identifier automatiquement les importations inutilisées pour les nettoyer.

La plupart des endroits où j'ai travaillé qui utilisent une quantité importante de Java font des importations explicites une partie de la norme de codage. Parfois, j'utilise toujours * pour le prototypage rapide, puis j'élargis les listes d'importation (certains IDE le feront également pour vous) lors de la production du code.

Josh Segall
la source
J'aime la plupart de vos points, mais c'est précisément le numéro 4 qui m'a poussé à voter pour votre réponse. Les IDE modernes suppriment la plupart des arguments contre l'utilisation d'importations explicites ...
Sheldon R.
Peut-être qu'une partie du problème ici est la façon dont les bibliothèques java standard sont disposées avec de nombreuses classes dans le même package. Par opposition à l'application d'un «principe de responsabilité unique» à un paquet.
LegendLength
11

Je préfère les importations spécifiques, car cela me permet de voir toutes les références externes utilisées dans le fichier sans regarder l'ensemble du fichier. (Oui, je sais que cela ne montrera pas nécessairement des références pleinement qualifiées. Mais je les évite autant que possible.)

Jeff C
la source
9

Dans un projet précédent, j'ai constaté que le passage de * -importations à des importations spécifiques réduisait le temps de compilation de moitié (d'environ 10 minutes à environ 5 minutes). L'import * * oblige le compilateur à rechercher dans chacun des packages répertoriés une classe correspondant à celle que vous avez utilisée. Bien que ce temps puisse être petit, il s'additionne pour les grands projets.

Un effet secondaire de l'importation * était que les développeurs copiaient et collaient les lignes d'importation courantes plutôt que de penser à ce dont ils avaient besoin.

Michael Hall
la source
11
Il devait y avoir beaucoup de lignes d'importation ou un système de développement vraiment pathétique pour que cela soit vrai. J'utilise import- * et je peux compiler l' intégralité de la base de code de 2107 classes en moins de 2 minutes.
Lawrence Dol
6

Dans le livre DDD

Quelle que soit la technologie de développement sur laquelle l'implémentation sera basée, recherchez des moyens de minimiser le travail de refactorisation des MODULES. En Java, il n'y a pas d'échappatoire à l'importation dans des classes individuelles, mais vous pouvez au moins importer des packages entiers à la fois, reflétant ainsi l'intention que les packages soient des unités hautement cohérentes tout en réduisant simultanément l'effort de modification des noms de package.

Et s'il encombre l'espace de noms local, ce n'est pas votre faute - blâmez la taille du paquet.

Ivan
la source
3
  • Il n'y a aucun impact sur l'exécution, car le compilateur remplace automatiquement * par des noms de classe concrets. Si vous décompilez le fichier .class, vous ne verriez jamais import ...*.

  • C # utilise toujours * (implicitement) car vous ne pouvez usingempaqueter que le nom. Vous ne pouvez jamais spécifier le nom de la classe. Java introduit la fonctionnalité après c #. (Java est tellement délicat à bien des égards, mais il dépasse ce sujet).

  • Dans Intellij Idea, lorsque vous "organisez les importations", il remplace automatiquement plusieurs importations du même package par *. Il s'agit d'une fonction obligatoire car vous ne pouvez pas la désactiver (bien que vous puissiez augmenter le seuil).

  • Le cas répertorié par la réponse acceptée n'est pas valide. Sans * vous avez toujours le même problème. Vous devez spécifier le nom de la pakcage dans votre code, que vous utilisiez * ou non.

Léon
la source
1
Dans IntelliJ, ce n'est pas une fonctionnalité obligatoire et elle peut être désactivée.
Bastien7
3

Pour mémoire: lorsque vous ajoutez une importation, vous indiquez également vos dépendances.

Vous pouvez voir rapidement quelles sont les dépendances des fichiers (à l'exclusion des classes du même espace de noms).

magallanes
la source
Se mettre d'accord. Le facteur de motivation n'est pas tant la performance ou la compilation, mais la lisibilité humaine de votre code. Imaginez simplement que vous lisez du code sans IDE - sur GitHub, par exemple. Soudain, la recherche de chaque référence non définie dans le fichier que vous lisez devient fastidieuse.
Leo Orientis
2

Le plus important est que l'importation java.awt.*peut rendre votre programme incompatible avec une future version Java:

Supposons que vous ayez une classe nommée "ABC", vous utilisez JDK 8 et vous importez java.util.*. Supposons maintenant que Java 9 sorte et qu'il ait une nouvelle classe dans le package java.utilqui, par coïncidence, se nomme également "ABC". Votre programme ne compilera plus sur Java 9, car le compilateur ne sait pas si avec le nom "ABC" vous voulez dire votre propre classe ou la nouvelle classe java.awt.

Vous n'aurez pas ce problème lorsque vous importerez uniquement les classes explicitement à partir de celles java.awtque vous utilisez réellement.

Ressources:

Importations Java

Affy
la source
3
astuce: vous auriez pu utiliser Streamcomme exemple d'une nouvelle classe ajoutée en Java dans java.util en Java 8 ...
Clint Eastwood
2

Parmi tous les points valables formulés des deux côtés, je n'ai pas trouvé ma principale raison d'éviter le caractère générique: j'aime pouvoir lire le code et savoir directement ce qu'est chaque classe, ou si sa définition n'est pas dans la langue ou le fichier, où le trouver. Si plusieurs packages sont importés avec *, je dois rechercher chacun d'eux pour trouver une classe que je ne reconnais pas. La lisibilité est suprême et j'accepte que le code ne nécessite pas d' IDE pour le lire.

user109439
la source
Si vous prenez cela à sa conclusion logique complète, votre style devrait être de ne pas utiliser du tout les importations et au lieu de "nouvelle LinkedList", utilisez toujours "new java.util.LinkedList" et faites-le de manière cohérente partout .
Erwin Smout