Por que o uso de um curinga com uma instrução de importação Java é ruim?

419

É muito mais conveniente e limpo usar uma única declaração como

import java.awt.*;

do que importar várias classes individuais

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

O que há de errado em usar um curinga na importinstrução?

jnancheta
fonte

Respostas:

518

O único problema é que ele atrapalha seu espaço de nomes local. Por exemplo, digamos que você esteja escrevendo um aplicativo Swing e, portanto java.awt.Event, precise e também esteja em interface com o sistema de calendário da empresa com.mycompany.calendar.Event. Se você importar os dois usando o método curinga, uma dessas três coisas acontece:

  1. Você tem um conflito total de nomes entre java.awt.Evente com.mycompany.calendar.Event, portanto, não pode nem compilar.
  2. Na verdade, você consegue importar apenas uma (apenas uma de suas duas importações importa .*), mas é a errada e luta para descobrir por que seu código está afirmando que o tipo está errado.
  3. Quando você compila seu código, não existe com.mycompany.calendar.Event, mas quando eles adicionam mais tarde, seu código válido anteriormente pára subitamente de compilar.

A vantagem de listar explicitamente todas as importações é que eu posso dizer rapidamente qual classe você pretende usar, o que simplesmente facilita a leitura do código. Se você está apenas fazendo uma coisa rápida e pontual, não há nada explicitamente errado , mas os futuros mantenedores agradecerão sua clareza.

Benjamin Pollack
fonte
7
É o primeiro cenário que vai acontecer. O compilador percebe que existem duas classes de eventos e fornece um erro.
jan.vdbergh
38
Verifique meu comentário abaixo - há um problema maior com tipos sendo adicionados a bibliotecas de terceiros ao longo do tempo. Você pode ter um código de compilação que para de compilar depois que alguém adiciona um tipo a um jar do qual você depende.
Scott Stanchfield
6
em relação ao problema 1: tecnicamente, você pode compilar, mas precisará usar o nome completo da classe a cada vez.
Kip
1
Você pode resolver esse tipo de conflito sem listar explicitamente todas as classes, o que causa problemas próprios.
precisa saber é o seguinte
196

Aqui está um voto para importações em estrela. Uma declaração de importação pretende importar um pacote , não uma classe. É muito mais limpo importar pacotes inteiros; os problemas aqui identificados (por exemplo, java.sql.Datevs java.util.Date) são facilmente remediados por outros meios, não são realmente abordados por importações específicas e certamente não justificam importações insanamente pedantes em todas as classes. Não há nada mais desconcertante do que abrir um arquivo de origem e ter que percorrer 100 instruções de importação.

Fazer importações específicas dificulta a refatoração; se você remover / renomear uma classe, precisará remover todas as suas importações específicas. Se você alternar uma implementação para uma classe diferente no mesmo pacote, precisará corrigir as importações. Embora essas etapas extras possam ser automatizadas, elas realmente atingem a produtividade sem nenhum ganho real.

Mesmo que o Eclipse não fizesse importações de classe por padrão, todo mundo continuaria fazendo importações em estrela. Sinto muito, mas realmente não há justificativa racional para fazer importações específicas.

Veja como lidar com conflitos de classe:

import java.sql.*;
import java.util.*;
import java.sql.Date;
davetron5000
fonte
28
Concordo. Embora não me opusesse a usar importações explícitas, ainda prefiro usar importações em estrela. Eles enfatizam que a "unidade de reutilização" é o pacote inteiro, não seus tipos individuais. As razões pelas quais outras pessoas listadas em relação às importações de estrelas são fracas e, na minha experiência, o uso de importações de estrelas nunca causou dificuldades reais.
Rogério
32
Veja javadude.com/articles/importondemandisevil.html para obter detalhes sobre por que é ruim. Idéia básica: pode causar código para parar de compilar quando as aulas são adicionados aos pacotes que importar (como quando Lista foi adicionado em java.util ...)
Scott Stanchfield
61
Todos os problemas mencionados podem ser resolvidos pelos IDEs modernos (ocultando importações, refatorando o nome da classe, etc ...).
Assilias
15
Eu não deveria ter que usar um IDE para ler ou escrever código-fonte - o código deve ser legível por si só, sem ferramentas especiais, a menos que a linguagem seja incrivelmente interessante. Nesse caso, o Java funciona muito bem - basta usar importações em estrela. Não há razão para não fazê-lo.
Davetron5000
42
@ davetron5000 Se o seu código contém mais de 10 importações de curingas e você usa classe Foo, e se eu leio seu código sem usar um IDE (já que seu argumento é que eu não deveria ter que usar um), como saberei de qual pacote Fooveio ? Claro, usando um IDE, o IDE me dirá, mas todo o seu argumento é que eu deveria poder ler o código sem um. Fazer importações explícitas ajuda a documentar o código (excelente motivo para evitar caracteres curinga) , e é muito mais provável que eu esteja lendo o código sem usar um IDE, do que escrevendo o código sem usar um IDE.
Andreas
169

consulte o meu artigo Import on Demand is Evil

Em suma, o maior problema é que seu código pode quebrar quando uma classe é adicionada a um pacote que você importa. Por exemplo:

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

// ...

List list;

No Java 1.1, tudo bem; A lista foi encontrada em java.awt e não houve conflito.

Agora, suponha que você verifique seu código que funciona perfeitamente e, um ano depois, alguém o traz para editá-lo e está usando o Java 1.2.

O Java 1.2 adicionou uma interface chamada List ao java.util. ESTRONDO! Conflito. O código que funciona perfeitamente não funciona mais.

Este é um recurso do idioma EVIL . Há NO razão que código deve parar de compilar apenas porque um tipo é adicionado a um pacote ...

Além disso, torna difícil para um leitor determinar qual "Foo" você está usando.

Scott Stanchfield
fonte
35
Esta não é uma desculpa válida. Se você estiver alterando a versão java, espera que algumas coisas falhem, o mesmo se você alterar a versão de um binário usado pelo seu código. Nestes casos, o código seria lançar um erro de compilação e é trivial para correção (veja a resposta anterior: stackoverflow.com/a/149282/7595 )
Pablo Fernandez
36
@ PabloFernandez - Não - se eu verificar o código que está no repositório há um ano, ele ainda deve ser compilado. A importação sob demanda pode falhar facilmente quando novas classes são adicionadas aos pacotes existentes importados. Não é apenas um problema ao atualizar versões Java. Além disso - se uma API for projetada bem, nunca deverá quebrar o código existente na atualização. O único momento em que precisei alterar o código ao atualizar as versões java foi por causa da importação sob demanda e quando a Sun inseriu as APIs XML no tempo de execução java.
Scott Stanchfield
3
ADICIONAR uma classe (com um nome exclusivo e totalmente qualificado!) Ao caminho da classe não deve afetar nada. O ponto aqui é que, se você não usar a sintaxe de importação sob demanda, ela não será. Portanto, não use a sintaxe ruim que o idioma infelizmente permite e esse é um problema menos real que você pode encontrar.
Scott Stanchfield
28
O ponto da minha resposta é que é um recurso desnecessário de linguagem que causa problemas. Muitos IDEs / editores lidam automaticamente com a expansão de importação. Use importações totalmente qualificadas e não há chance de esse erro específico acontecer. Fui atingido por este quando estava sob pressão para corrigir um erro no código existente, e você realmente não precisa de algo assim como uma distração da tarefa real em mãos. java.util.Listvs java.awt.Listnão é tão ruim de entender, mas tente quando o nome da classe for Configuratione várias bibliotecas de dependência o adicionaram em sua versão mais recente do repositório maven.
Scott Stanchfield
5
Se eu atualizar um jar em que as classes que eu uso sejam compatíveis com API forward e E eu não usar sintaxe de importação sob demanda, isso não me afetará. Isso faz sentido para você? Não tenha preguiça de definir importações e isso não é problema. A sintaxe de importação sob demanda foi um erro na definição da linguagem Java; uma linguagem razoável não deve permitir erros como esse.
22412 Scott Scott -chichfield
67

É não ruim para usar um wild card com uma declaração de importação Java.

Em Código Limpo , Robert C. Martin realmente recomenda usá-los para evitar longas listas de importação.

Aqui está a recomendação:

J1: Evite longas listas de importação usando caracteres curinga

Se você usar duas ou mais classes de um pacote, importe o pacote inteiro com

pacote de importação. *;

Longas listas de importações são assustadoras para o leitor. Não queremos desordenar as partes superiores dos nossos módulos com 80 linhas de importação. Em vez disso, queremos que as importações sejam uma declaração concisa sobre com quais pacotes colaboramos.

Importações específicas são dependências complexas, enquanto importações curinga não são. Se você importar especificamente uma classe, essa classe deverá existir. Mas se você importar um pacote com um curinga, nenhuma classe específica precisará existir. A declaração de importação simplesmente adiciona o pacote ao caminho de pesquisa ao procurar nomes. Portanto, nenhuma dependência verdadeira é criada por essas importações e, portanto, elas servem para manter nossos módulos menos acoplados.

Há momentos em que a longa lista de importações específicas pode ser útil. Por exemplo, se você está lidando com código legado e deseja descobrir para quais classes precisa criar zombarias e stubs, você pode percorrer a lista de importações específicas para descobrir os verdadeiros nomes qualificados de todas essas classes e depois colocar os stubs apropriados no lugar. No entanto, esse uso para importações específicas é muito raro. Além disso, a maioria dos IDEs modernos permitirá converter as importações curinga em uma lista de importações específicas com um único comando. Portanto, mesmo no caso de legado, é melhor importar caracteres curinga.

Às vezes, as importações de curingas podem causar conflitos de nome e ambiguidades. Duas classes com o mesmo nome, mas em pacotes diferentes, precisarão ser importadas especificamente ou, pelo menos, qualificadas especificamente quando usadas. Isso pode ser um incômodo, mas é raro o suficiente que o uso de importações de caracteres curinga ainda seja geralmente melhor do que importações específicas.

hwiechers
fonte
41
Sugiro a Robert C. Martin que use padrões melhores para criar pacotes e classes mais concisos que não exijam 80 linhas de importação. Que muitas classes necessárias para importação dentro de uma única classe estão apenas implorando 'Entropy, Entropy, break me please ...' e aponta o motivo para evitar a importação * descrita nas respostas de Scott Stanchfields
Ray
28
Por mais que eu geralmente ame o que o tio Bob tem a dizer, neste caso também tenho que discordar dele.
33
Longas listas de importações são assustadoras para o leitor. - Esta afirmação tem uma presunção inválida. Os programadores não precisam ler o código-fonte de cima para baixo. Podemos não ler as listas de importação. Quando o fazemos, podemos ler apenas uma das importações, para esclarecimentos. Em outros momentos, as importações podem ser totalmente reduzidas, se estivermos trabalhando em um IDE. Independentemente da fonte, hoje este é um mau conselho.
Andy Thomas
10
Apenas para fornecer algum contrapeso quando se trata de citar autoridades sobre esse assunto: o Google Java Style Guide e o Java Style Guide do Twitter (que é amplamente baseado no Google, para ser justo) proíbem especificamente importações de caracteres curinga. Mas eles não fornecem nenhuma justificativa para essa decisão.
Outro # nó
1
Provavelmente, o único ponto em que não concordei no Código Limpo. É preciso percorrer algumas linhas de instruções de importação ou se esforçar para descobrir de onde a classe vem. Prefiro identificar facilmente de onde vem uma determinada classe.
Ganesh Satpute
27

atuação : nenhum impacto no desempenho, pois o código de bytes é o mesmo. embora isso leve a algumas despesas gerais de compilação.

Compilação : na minha máquina pessoal, compilar uma classe em branco sem importar nada leva 100 ms, mas a mesma classe ao importar java. * Leva 170 ms.

Vinish Nain
fonte
3
import java.*não importa nada. Por que isso faria diferença?
Marquês de Lorne #
6
Faz a diferença porque é pesquisado durante a compilação.
usar o seguinte comando
25

Isso atrapalha seu espaço de nome, exigindo que você especifique completamente todos os nomes de classe ambíguos. A ocorrência mais comum disso é com:

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

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

Também ajuda a tornar suas dependências concretas, pois todas as dependências estão listadas na parte superior do arquivo.

hazzen
fonte
20
  1. Ajuda a identificar conflitos de nome de classe: duas classes em pacotes diferentes com o mesmo nome. Isso pode ser mascarado com a * importação.
  2. Torna as dependências explícitas, para que qualquer pessoa que precise ler seu código posteriormente saiba o que você quis importar e o que não quis importar.
  3. Isso pode tornar a compilação mais rápida porque o compilador não precisa pesquisar o pacote inteiro para identificar desvios, embora isso geralmente não seja um grande problema para os compiladores modernos.
  4. Os aspectos inconvenientes das importações explícitas são minimizados com os IDEs modernos. A maioria dos IDEs permite recolher a seção de importação para que não ocorra, preencher automaticamente as importações quando necessário e identificar automaticamente as importações não usadas para ajudar a limpá-las.

A maioria dos lugares em que trabalhei que usam qualquer quantidade significativa de Java faz com que importações explícitas façam parte do padrão de codificação. Às vezes, ainda uso * para prototipagem rápida e, em seguida, expanda as listas de importação (alguns IDEs também farão isso por você) ao produzir o código.

Josh Segall
fonte
Gosto da maioria dos seus pontos, mas foi o número 4 especificamente que me levou a votar sua resposta. IDEs modernas remover a maioria dos argumentos contra o uso de importações explícitas ...
Sheldon R.
Talvez parte do problema aqui seja a maneira como as bibliotecas java padrão são organizadas com muitas classes dentro do mesmo pacote. Em vez de aplicar mais um "princípio de responsabilidade única" a um pacote.
usar o seguinte comando
11

Eu prefiro importações específicas, porque elas permitem ver todas as referências externas usadas no arquivo sem examinar o arquivo inteiro. (Sim, eu sei que não mostrará necessariamente referências totalmente qualificadas. Mas eu as evito sempre que possível.)

Jeff C
fonte
9

Em um projeto anterior, descobri que a mudança de importações * para importações específicas reduzia o tempo de compilação pela metade (de cerca de 10 minutos para cerca de 5 minutos). O * -import faz com que o compilador pesquise cada um dos pacotes listados para uma classe correspondente à que você usou. Embora esse tempo possa ser pequeno, ele contribui para grandes projetos.

Um efeito colateral da importação * era que os desenvolvedores copiavam e colavam linhas de importação comuns em vez de pensar no que precisavam.

Michael Hall
fonte
11
Deve ter havido muitas linhas de importação ou um sistema de desenvolvimento realmente patético para que isso seja verdade. Eu uso import- * e posso compilar toda a minha base de código de 2107 classes em menos de 2 minutos.
Lawrence Dol
6

No livro DDD

Seja qual for a tecnologia de desenvolvimento na qual a implementação se baseie, procure maneiras de minimizar o trabalho de refatoração de MODULES. Em Java, não há como escapar da importação para classes individuais, mas você pode pelo menos importar pacotes inteiros por vez, refletindo a intenção de que os pacotes sejam unidades altamente coesas, reduzindo simultaneamente o esforço de alterar os nomes dos pacotes.

E se ele confunde o espaço de nomes local, a culpa não é sua - culpe o tamanho do pacote.

Ivan
fonte
3
  • Não há impacto no tempo de execução, pois o compilador substitui automaticamente o * por nomes de classes concretos. Se você descompilar o arquivo .class, nunca o verá import ...*.

  • O C # sempre usa * (implicitamente), pois você pode apenas o usingnome do pacote. Você nunca pode especificar o nome da classe. Java introduz o recurso após c #. (Java é tão complicado em muitos aspectos, mas está além deste tópico).

  • No Intellij Idea, quando você "organiza importações", ele substitui automaticamente várias importações do mesmo pacote por *. Esse é um recurso obrigatório, pois você não pode desativá-lo (embora possa aumentar o limite).

  • O caso listado pela resposta aceita não é válido. Sem * você ainda tem o mesmo problema. Você precisa especificar o nome do pakcage no seu código, independentemente de você usar * ou não.

Leon
fonte
1
No IntelliJ, não é um recurso obrigatório e pode ser desativado.
Bastien7
3

Para o registro: Ao adicionar uma importação, você também indica suas dependências.

Você pode ver rapidamente quais são as dependências dos arquivos (excluindo classes do mesmo espaço para nome).

Magallanes
fonte
Aceita. O motivador não é tanto desempenho ou compilação, mas legibilidade humana do seu código. Imagine que você está lendo código sem um IDE - no GitHub, por exemplo. De repente, procurar todas as referências não definidas no arquivo que você está lendo se torna entediante.
Leo Orientis
2

O mais importante é que a importação java.awt.*pode tornar seu programa incompatível com uma versão futura do Java:

Suponha que você tenha uma classe chamada "ABC", esteja usando o JDK 8 e importando java.util.*. Agora, suponha que o Java 9 seja lançado e tenha uma nova classe no pacote java.utilque por coincidência também seja chamada de "ABC". Seu programa agora não será compilado no Java 9, porque o compilador não sabe se, com o nome "ABC", você quer dizer sua própria classe ou a nova classe emjava.awt .

Você não terá esse problema quando importar apenas essas classes explicitamente java.awtdaquilo que realmente usa.

Recursos:

Importações Java

Affy
fonte
3
dica: você poderia ter usado Streamcomo um exemplo de uma nova classe adicionada em Java no java.util no Java 8 ... #
Clint Eastwood
2

Entre todos os argumentos válidos dos dois lados, não encontrei o principal motivo para evitar o curinga: gosto de poder ler o código e saber diretamente o que são todas as classes, ou se a definição não está no idioma ou no idioma. o arquivo, onde encontrá-lo. Se mais de um pacote for importado com *, preciso procurar em cada um deles para encontrar uma classe que não reconheço. A legibilidade é suprema e eu concordo que o código não deve exigir um IDE para lê-lo.

user109439
fonte
Se você levar isso à sua conclusão lógica completa, seu estilo será não usar importações e, em vez de "new LinkedList", use sempre "new java.util.LinkedList" e faça isso de maneira consistente em todos os lugares .
Erwin Smout 28/09