¿Por qué es malo usar un comodín con una declaración de importación de Java?

419

Es mucho más conveniente y limpio usar una sola declaración como

import java.awt.*;

que importar un montón de clases individuales

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

¿Qué tiene de malo usar un comodín en la importdeclaración?

jnancheta
fuente

Respuestas:

518

El único problema es que satura tu espacio de nombres local. Por ejemplo, supongamos que está escribiendo una aplicación Swing, y por lo tanto necesita java.awt.Event, y también está interactuando con el sistema de calendario de la compañía, que tiene com.mycompany.calendar.Event. Si importa ambos utilizando el método comodín, ocurre una de estas tres cosas:

  1. Tiene un conflicto de nombres absoluto entre java.awt.Eventy com.mycompany.calendar.Event, por lo que ni siquiera puede compilar.
  2. En realidad, solo logra importar uno (solo uno de sus dos importaciones lo hace .*), pero es el incorrecto y le cuesta descubrir por qué su código afirma que el tipo es incorrecto.
  3. Cuando compila su código, no hay ninguno com.mycompany.calendar.Event, pero cuando luego agregan uno, su código previamente válido deja de compilarse.

La ventaja de enumerar explícitamente todas las importaciones es que puedo decir de un vistazo qué clase quería usar, lo que simplemente hace que la lectura del código sea mucho más fácil. Si solo está haciendo algo rápido, no hay nada explícitamente incorrecto , pero los futuros mantenedores le agradecerán su claridad de lo contrario.

Benjamin Pollack
fuente
77
Es el primer escenario que sucederá. El compilador nota que hay dos clases de eventos y da un error.
jan.vdbergh
38
Asegúrese de revisar mi comentario a continuación: hay un problema mayor con los tipos que se agregan a bibliotecas de terceros con el tiempo. Puede tener un código de compilación que deje de compilarse después de que alguien agregue un tipo a un jar del que depende.
Scott Stanchfield
66
con respecto al problema 1: técnicamente, puede compilar, pero deberá usar el nombre de clase completo cada vez.
Kip
1
Puede resolver este tipo de conflictos sin enumerar cada clase explícitamente, lo que causa problemas propios.
rpjohnst
196

Aquí hay un voto para las importaciones estrella. Una declaración de importación está destinada a importar un paquete , no una clase. Es mucho más limpio importar paquetes completos; Los problemas identificados aquí (p. ej., java.sql.Datevs java.util.Date) se solucionan fácilmente por otros medios, no realmente abordados por importaciones específicas y ciertamente no justifican importaciones increíblemente pedantes en todas las clases. No hay nada más desconcertante que abrir un archivo fuente y tener que pasar por 100 declaraciones de importación.

Hacer importaciones específicas hace que la refactorización sea más difícil; Si elimina / cambia el nombre de una clase, debe eliminar todas sus importaciones específicas. Si cambia una implementación a una clase diferente en el mismo paquete, debe arreglar las importaciones. Si bien estos pasos adicionales se pueden automatizar, son realmente éxitos de productividad sin ganancia real.

Incluso si Eclipse no hiciera importaciones de clase por defecto, todos seguirían haciendo importaciones estrella. Lo siento, pero realmente no hay justificación racional para realizar importaciones específicas.

Aquí se explica cómo lidiar con los conflictos de clase:

import java.sql.*;
import java.util.*;
import java.sql.Date;
davetron5000
fuente
28
Estoy de acuerdo. Aunque no me opondría a usar importaciones explícitas, todavía prefiero usar importaciones estrella. Enfatizan que la "unidad de reutilización" es el paquete completo, no sus tipos individuales. Las razones por las que otros figuran en contra de las importaciones de estrellas son débiles, y en mi experiencia el uso de importaciones de estrellas nunca ha causado dificultades reales.
Rogério
32
Consulte javadude.com/articles/importondemandisevil.html para obtener detalles sobre por qué es malo. Idea básica: puede hacer que el código deje de compilarse cuando se agregan clases a los paquetes que importa (como cuando se agregó List a java.util ...)
Scott Stanchfield
61
Todos los problemas que menciona pueden resolverse mediante IDEs modernos (ocultar importaciones, refactorizar el nombre de la clase, etc.).
Assylias
15
No debería tener que usar un IDE para leer o escribir el código fuente: el código debería ser legible por sí solo sin herramientas especiales a menos que el lenguaje sea increíblemente inteligente. En este caso, Java funciona muy bien, solo use las importaciones estrella. No hay razón para no hacerlo.
davetron5000
42
@ davetron5000 Si su código contiene más de 10 importaciones de comodines, y usa la clase Foo, y si leo su código sin usar un IDE (ya que su argumento es que no debería tener que usar uno), ¿cómo sabré de qué paquete Fooproviene? ? Claro, usando un IDE, el IDE me lo dirá, pero su argumento completo es que debería poder leer el código sin uno. Hacer importaciones explícitas ayuda a documentar el código (una buena razón para evitar los comodines) , y es mucho más probable que esté leyendo el código sin usar un IDE, que escribir el código sin usar un IDE.
Andreas
169

por favor vea mi artículo Importar bajo demanda es malo

En resumen, el mayor problema es que su código puede romperse cuando se agrega una clase a un paquete que importa. Por ejemplo:

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

// ...

List list;

En Java 1.1, esto estaba bien; La lista se encontró en java.awt y no hubo conflicto.

Ahora suponga que registra su código que funciona perfectamente, y un año después alguien más lo saca para editarlo y está utilizando Java 1.2.

Java 1.2 agregó una interfaz llamada Lista a java.util. ¡AUGE! Conflicto. El código que funciona perfectamente ya no funciona.

Esta es una característica del lenguaje MAL . Hay NO razón de que el código debe dejar de compilar sólo porque un tipo se añadió a un paquete de ...

Además, hace que sea difícil para un lector determinar qué "Foo" está utilizando.

Scott Stanchfield
fuente
35
Esta no es una excusa válida. Si está cambiando la versión de Java, de alguna manera espera que algunas cosas fallen, lo mismo si cambia la versión de un binario que usa su código. En estos casos, el código arrojaría un error de compilación y es trivial de arreglar (vea la respuesta anterior: stackoverflow.com/a/149282/7595 )
Pablo Fernández,
36
@PabloFernandez - No: si reviso el código que ha estado en un repositorio durante un año, aún debería compilarse. La importación bajo demanda puede fallar fácilmente cuando se agregan nuevas clases a los paquetes existentes que he importado. No es solo un problema al actualizar las versiones de Java. Además, si una API está bien diseñada, nunca debería romper el código existente en la actualización. La única vez que necesitaba cambiar el código cuando actualizaba las versiones de Java era por importación bajo demanda y cuando Sun sacaba las API XML al tiempo de ejecución de Java.
Scott Stanchfield
3
AGREGAR una clase (¡con un nombre único y completamente calificado!) Al classpath no debería afectar nada. El punto aquí es que si no utiliza la sintaxis de importación bajo demanda, no lo hará. Así que no use la sintaxis incorrecta que desafortunadamente el lenguaje permite y este es un problema menos real con el que puede ser golpeado.
Scott Stanchfield
28
El punto de mi respuesta es que es una característica de lenguaje innecesaria que causa problemas. Muchos IDE / editores manejan automáticamente la expansión de importación. Use importaciones totalmente calificadas y no hay posibilidad de que ocurra este error en particular. He sido golpeado por este cuando estoy bajo presión para corregir un error en el código existente, y realmente no necesitas algo como esto como una distracción de la tarea real en cuestión. java.util.Listvs java.awt.Listno es tan malo de entender, pero pruébelo cuando el nombre de la clase lo sea Configurationy múltiples bibliotecas de dependencias lo hayan agregado en su última versión de repositorio maven.
Scott Stanchfield
55
Si actualizo un archivo jar donde las clases que uso son compatibles con API-forward, y no uso la sintaxis de importación bajo demanda, no me afectará en absoluto. ¿Tiene sentido para ti? No sea flojo al definir las importaciones y esto no es un problema. La sintaxis de importación bajo demanda fue un error en la definición del lenguaje Java; Un lenguaje razonable no debe permitir errores como este.
Scott Stanchfield
67

Es no mal usar un comodín con una declaración de importación de Java.

En Clean Code , Robert C. Martin realmente recomienda usarlos para evitar largas listas de importación.

Aquí está la recomendación:

J1: Evite largas listas de importación utilizando comodines

Si usa dos o más clases de un paquete, importe el paquete completo con

paquete de importación. *;

Las largas listas de importaciones son desalentadoras para el lector. No queremos saturar la parte superior de nuestros módulos con 80 líneas de importación. Más bien queremos que las importaciones sean una declaración concisa sobre con qué paquetes colaboramos.

Las importaciones específicas son dependencias difíciles, mientras que las importaciones con comodines no lo son. Si importa específicamente una clase, entonces esa clase debe existir. Pero si importa un paquete con un comodín, no es necesario que existan clases particulares. La declaración de importación simplemente agrega el paquete a la ruta de búsqueda cuando busca nombres. Por lo tanto, tales importaciones no crean una dependencia verdadera y, por lo tanto, sirven para mantener nuestros módulos menos acoplados.

Hay momentos en que la larga lista de importaciones específicas puede ser útil. Por ejemplo, si está lidiando con código heredado y desea averiguar para qué clases necesita construir simulacros y trozos, puede recorrer la lista de importaciones específicas para descubrir los verdaderos nombres calificados de todas esas clases y luego colocar los trozos apropiados en su lugar. Sin embargo, este uso para importaciones específicas es muy raro. Además, la mayoría de los IDE modernos le permitirán convertir las importaciones con comodines en una lista de importaciones específicas con un solo comando. Entonces, incluso en el caso heredado, es mejor importar comodines.

Las importaciones de comodines a veces pueden causar conflictos de nombres y ambigüedades. Dos clases con el mismo nombre, pero en diferentes paquetes, deberán importarse específicamente, o al menos calificarse específicamente cuando se usen. Esto puede ser una molestia, pero es lo suficientemente raro como para que el uso de importaciones con comodines sea generalmente mejor que las importaciones específicas.

hwiechers
fuente
41
Sugeriría a Robert C. Martin que use mejores patrones para crear paquetes y clases más concisos que no requieran 80 líneas de importación. Que muchas clases necesarias para importar dentro de una sola clase solo están pidiendo 'Entropía, Entropía, rómpeme por favor ...' y señala la razón para evitar la importación * descrita en las respuestas de Scott Stanchfields
Ray
28
Por mucho que me encanta lo que dice el tío Bob, en este caso también tengo que estar en desacuerdo con él.
33
Las largas listas de importaciones son desalentadoras para el lector. - Esta afirmación tiene una presunción no válida. Los programadores no están obligados a leer el código fuente de arriba a abajo. Es posible que no leamos listas de importación en absoluto. Cuando lo hagamos, podríamos leer solo una de las importaciones, para aclaración. En otras ocasiones, las importaciones pueden colapsarse por completo, si estamos trabajando en un IDE. Independientemente de la fuente, hoy este es un mal consejo.
Andy Thomas
10
Sólo para proporcionar algún contrapeso cuando adentro viene a las autoridades citan en este tema: La Guía de estilo de Google de Java , así como guía de estilo de Java de Twitter (que se basa en gran medida en el Google uno, para ser justos) prohíben específicamente las importaciones comodín. Pero no proporcionan ninguna justificación para esta decisión.
anothernode
1
Probablemente el único punto que no estuve de acuerdo en Clean Code. Es tener que desplazarse por algunas líneas de declaraciones de importación o luchar para encontrar de dónde viene la clase. Prefiero identificar fácilmente de dónde proviene cierta clase.
Ganesh Satpute
27

Rendimiento : sin impacto en el rendimiento, ya que el código de bytes es el mismo. aunque conducirá a algunos gastos generales de compilación.

Compilación : en mi máquina personal, compilar una clase en blanco sin importar nada toma 100 ms, pero la misma clase cuando import java. * Toma 170 ms.

Vinish Nain
fuente
3
import java.*No importa nada. ¿Por qué haría una diferencia?
Marqués de Lorne
66
Hace una diferencia porque se busca durante la compilación.
LegendLength
25

Abarrota su espacio de nombres, lo que requiere que especifique completamente cualquier nombre de clase que sea ambiguo. La ocurrencia más común de esto es con:

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

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

También ayuda a concretar sus dependencias, ya que todas sus dependencias se enumeran en la parte superior del archivo.

hazzen
fuente
20
  1. Ayuda a identificar conflictos de nombre de clase: dos clases en diferentes paquetes que tienen el mismo nombre. Esto se puede enmascarar con * import.
  2. Hace explícitas las dependencias, de modo que cualquiera que tenga que leer su código más tarde sepa lo que quiso importar y lo que no quiso importar.
  3. Puede hacer una compilación más rápida porque el compilador no tiene que buscar en todo el paquete para identificar las depencias, aunque esto generalmente no es un gran problema con los compiladores modernos.
  4. Los aspectos inconvenientes de las importaciones explícitas se minimizan con los IDE modernos. La mayoría de los IDE le permiten contraer la sección de importación para que no se interponga, llenar automáticamente las importaciones cuando sea necesario e identificar automáticamente las importaciones no utilizadas para ayudar a limpiarlas.

La mayoría de los lugares en los que he trabajado que usan una cantidad significativa de Java hacen que las importaciones explícitas sean parte del estándar de codificación. A veces sigo usando * para la creación rápida de prototipos y luego amplío las listas de importación (algunos IDE también lo harán por usted) al crear el código.

Josh Segall
fuente
Me gustan la mayoría de sus puntos, pero fue el # 4 específicamente el que me hizo votar su respuesta. Los IDE modernos eliminan la mayoría de los argumentos en contra del uso de importaciones explícitas ...
Sheldon R.
Quizás parte del problema aquí es la forma en que las bibliotecas estándar de Java se presentan con muchas clases dentro del mismo paquete. A diferencia de aplicar más de un "principio de responsabilidad única" a un paquete.
LegendLength
11

Prefiero importaciones específicas, porque me permite ver todas las referencias externas utilizadas en el archivo sin mirar el archivo completo. (Sí, sé que no necesariamente mostrará referencias totalmente calificadas. Pero las evito siempre que sea posible).

Jeff C
fuente
9

En un proyecto anterior descubrí que el cambio de importaciones * a importaciones específicas reducía el tiempo de compilación a la mitad (de aproximadamente 10 minutos a aproximadamente 5 minutos). La importación * hace que el compilador busque en cada uno de los paquetes listados una clase que coincida con la que usó. Si bien este tiempo puede ser pequeño, se suma para grandes proyectos.

Un efecto secundario de la importación * fue que los desarrolladores copiarían y pegarían líneas de importación comunes en lugar de pensar en lo que necesitaban.

Michael Hall
fuente
11
Debe haber muchas líneas de importación o un sistema de desarrollo realmente patético para que esto sea cierto. Uso import- * y puedo compilar toda mi base de código de 2107 clases en menos de 2 minutos.
Lawrence Dol
6

En el libro DDD

En cualquier tecnología de desarrollo en la que se basará la implementación, busque formas de minimizar el trabajo de refactorización de MÓDULOS. En Java, no se puede escapar de la importación a clases individuales, pero al menos puede importar paquetes completos a la vez, lo que refleja la intención de que los paquetes sean unidades altamente cohesivas y al mismo tiempo reduzca el esfuerzo de cambiar los nombres de los paquetes.

Y si abarrota el espacio de nombres local, no es su culpa, culpe al tamaño del paquete.

Ivan
fuente
3
  • No hay impacto en el tiempo de ejecución, ya que el compilador reemplaza automáticamente el * con nombres de clase concretos. Si descompila el archivo .class, nunca lo vería import ...*.

  • C # siempre usa * (implícitamente) ya que solo puede usingempaquetar el nombre. Nunca puede especificar el nombre de la clase en absoluto. Java presenta la función después de c #. (Java es muy complicado en muchos aspectos, pero está más allá de este tema).

  • En Intellij Idea, cuando "organiza las importaciones", reemplaza automáticamente las importaciones múltiples del mismo paquete con *. Esta es una característica obligatoria, ya que no puede desactivarla (aunque puede aumentar el umbral).

  • El caso que figura en la respuesta aceptada no es válido. Sin * todavía tienes el mismo problema. Debe especificar el nombre del paquete en su código sin importar si usa * o no.

León
fuente
1
En IntelliJ, no es una función obligatoria y se puede desactivar.
Bastien7
3

Para el registro: cuando agrega una importación, también está indicando sus dependencias.

Podrías ver rápidamente cuáles son las dependencias de los archivos (excluyendo las clases del mismo espacio de nombres).

magallanes
fuente
De acuerdo. El motivador no es tanto el rendimiento o la compilación, sino la legibilidad humana de su código. Imagine que está leyendo código sin un IDE, por ejemplo, en GitHub. Buscar de repente cada referencia no definida en el archivo que estás leyendo se vuelve tremendamente tedioso.
Leo Orientis
2

La más importante es que la importación java.awt.*puede hacer que su programa sea incompatible con una futura versión de Java:

Suponga que tiene una clase llamada "ABC", está utilizando JDK 8 e importa java.util.*. Ahora, supongamos que sale Java 9 y tiene una nueva clase en el paquete java.utilque por casualidad también se llama "ABC". Su programa ahora no se compilará en Java 9, porque el compilador no sabe si con el nombre "ABC" quiere decir su propia clase o la nueva clase en java.awt.

No tendrá ese problema cuando importe solo aquellas clases explícitamente de las java.awtque realmente usa.

Recursos:

Importaciones Java

Affy
fuente
3
consejo: podrías haber usado Streamcomo ejemplo una nueva clase agregada en Java en java.util en Java 8 ...
Clint Eastwood
2

Entre todos los puntos válidos hechos en ambos lados, no he encontrado mi razón principal para evitar el comodín: me gusta poder leer el código y saber directamente qué es cada clase, o si su definición no está en el idioma o el archivo, donde encontrarlo. Si se importa más de un paquete con *, tengo que buscar en cada uno de ellos para encontrar una clase que no reconozca. La legibilidad es suprema, y ​​estoy de acuerdo en que el código no debería requerir un IDE para leerlo.

usuario109439
fuente
Si lleva esto a su conclusión lógica completa, entonces su estilo debería ser no usar importaciones en absoluto y en lugar de "new LinkedList" siempre use "new java.util.LinkedList" y haga esto de manera consistente en todas partes .
Erwin Smout