Почему плохо использовать подстановочный знак с оператором импорта Java?


419

Гораздо удобнее и понятнее использовать одно утверждение типа

import java.awt.*;

чем импортировать кучу отдельных классов

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

Что не так с использованием подстановочного знака в importутверждении?

Ответы:


518

Единственная проблема в том, что он загромождает ваше локальное пространство имен. Например, предположим, что вы пишете приложение на Swing, и вам нужно java.awt.Event, а также взаимодействует с системой календаря компании, которая имеет com.mycompany.calendar.Event. Если вы импортируете оба метода подстановки, происходит одно из следующих трех событий:

  1. У вас прямой конфликт имен между java.awt.Eventи com.mycompany.calendar.Event, поэтому вы даже не можете скомпилировать.
  2. На самом деле вам удается импортировать только один (это делает только один из двух ваших импортов .*), но это неправильный вариант, и вы изо всех сил пытаетесь выяснить, почему ваш код утверждает, что тип неправильный.
  3. Когда вы компилируете свой код, его нет com.mycompany.calendar.Event, но когда он позже добавляется, ваш ранее действительный код внезапно прекращает компиляцию.

Преимущество явного перечисления всех импортов состоит в том, что я могу сразу определить, какой класс вы намеревались использовать, что просто делает чтение кода намного проще. Если вы просто делаете быструю разовую вещь, в этом нет ничего явно неправильного , но будущие сопровождающие будут благодарить вас за вашу ясность в противном случае.


7
Это первый сценарий, который случится. Компилятор замечает, что есть два класса Event, и выдает ошибку.
Jan.vdbergh

38
Не забудьте проверить мой комментарий ниже - существует большая проблема с типами, добавляемыми в сторонние библиотеки со временем. У вас может быть компилируемый код, который перестает компилироваться после того, как кто-то добавляет тип к банке, от которой вы зависите.
Скотт Стэнчфилд,

6
по вопросу 1: технически вы можете скомпилировать, но вам придется каждый раз использовать полное имя класса.
Кип

1
Вы можете разрешать конфликты такого рода без явного перечисления каждого класса, что вызывает собственные проблемы.
rpjohnst

196

Вот голос за звездный импорт. Оператор импорта предназначен для импорта пакета , а не класса. Гораздо чище импортировать целые пакеты; выявленные здесь проблемы (например, java.sql.Datevs java.util.Date) легко устраняются другими средствами, которые на самом деле не решаются с помощью конкретного импорта и, конечно, не оправдывают безумно педантичный импорт для всех классов. Нет ничего более смущающего, чем открытие исходного файла и необходимость пролистать 100 операторов импорта.

Выполнение определенного импорта затрудняет рефакторинг; если вы удаляете / переименовываете класс, вам нужно удалить все его специфические импорта. Если вы переключаете реализацию на другой класс в том же пакете, вы должны исправить импорт. Несмотря на то, что эти дополнительные шаги могут быть автоматизированы, они действительно являются хитами производительности без реальной выгоды.

Даже если Eclipse не выполняет импорт классов по умолчанию, все равно будут выполнять импорт звездочек. Извините, но на самом деле нет рационального обоснования для выполнения конкретного импорта.

Вот как бороться с конфликтами классов:

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

28
Согласен. Хотя я не был бы против использования явного импорта, я все же предпочитаю использовать звездный импорт. Они подчеркивают, что «единицей повторного использования» является весь пакет, а не его отдельные типы. Причины, перечисленные другими против импорта звезд, слабы, и, по моему опыту, использование импорта звезд никогда не вызывало реальных трудностей.
Rogério

32
См. Javadude.com/articles/importondemandisevil.html, чтобы узнать, почему это зло. Основная идея: это может привести к остановке компиляции кода при добавлении классов в импортируемые вами пакеты (например, когда List был добавлен в java.util ...)
Скотт Стэнчфилд,

61
Все проблемы, которые вы упомянули, могут быть решены с помощью современных IDE (скрытие импорта, рефакторинг имени класса и т. Д.).
assylias

15
Мне не нужно было использовать IDE для чтения или записи исходного кода - код должен быть читаемым сам по себе без специальных инструментов, если язык не невероятно умный. В этом случае Java прекрасно работает - просто используйте импорт звездочек. Нет причин не делать этого
davetron5000

42
@ davetron5000 Если ваш код содержит более 10 импортов с подстановочными знаками, и вы используете класс Foo, и если я прочитал ваш код без использования IDE (поскольку вы утверждаете, что мне не нужно его использовать), как я узнаю, из какого пакета Fooпришел ? Конечно, используя IDE, IDE скажет мне, но весь ваш аргумент в том, что я смогу прочитать код без такового. Выполнение явного импорта помогает документировать код (хорошая причина избегать подстановочных знаков) , и гораздо более вероятно, что я буду читать код без использования IDE, чем то, что я буду писать код без использования IDE.
Андреас

169

пожалуйста, смотрите мою статью « Импорт по требованию - зло»

Короче говоря, самая большая проблема в том , что ваш код может сломаться , когда класс добавляется к пакету импортируемого. Например:

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

// ...

List list;

В Java 1.1 это было нормально; Список был найден в java.awt и не было никакого конфликта.

Теперь предположим, что вы зарегистрировали свой отлично работающий код, а год спустя кто-то другой выдает его для редактирования и использует Java 1.2.

Java 1.2 добавила интерфейс с именем List в java.util. БУМ! Конфликт. Отлично работающий код больше не работает.

Это особенность злого языка. Там нет НИКАКОЙ причины того, что код должен прекратить сбор только потому тип добавляется в пакет ...

Кроме того, читателю трудно определить, какой «Foo» вы используете.


35
Это не оправдание. Если вы меняете версию Java, вы ожидаете, что некоторые вещи потерпят неудачу, то же самое, если вы измените версию двоичного файла, который использует ваш код. В этих случаях код выдаст ошибку компиляции, и ее тривиально исправить (см. Предыдущий ответ: stackoverflow.com/a/149282/7595 )
Пабло Фернандес,

36
@PabloFernandez - Нет - если я проверю код, который был в хранилище в течение года, он все равно должен скомпилироваться. Импорт по требованию может легко потерпеть неудачу, когда новые классы добавляются в существующие пакеты, которые я импортировал. Это не просто проблема при обновлении версий Java. Кроме того - если API разработан хорошо, он никогда не должен нарушать существующий код при обновлении. Единственное время, когда мне нужно было изменить код при обновлении версий Java, было из-за импорта по требованию и когда Sun вытащил API-интерфейсы XML в среду выполнения Java.
Скотт Стэнчфилд

3
Добавление класса (с уникальным, полностью определенным именем!) В путь к классам не должно ни на что влиять. Дело в том, что если вы не используете синтаксис импорта по требованию, это не так. Так что не используйте плохой синтаксис, который, к сожалению, допускает язык, и это еще одна реальная проблема, с которой вы можете столкнуться.
Скотт Стэнчфилд

28
Суть моего ответа в том, что это лишняя языковая функция, которая вызывает проблемы. Многие IDE / редакторы автоматически обрабатывают расширение импорта. Используйте импорт с полной квалификацией, и вероятность возникновения этой конкретной ошибки отсутствует. Я был поражен этим, когда находился под давлением, чтобы исправить ошибку в существующем коде, и вам действительно не нужно что-то подобное, как отвлечение от реальной задачи под рукой. java.util.Listvs java.awt.Listне так уж плохо понять, но попробуйте, когда имя класса Configurationи несколько библиотек зависимостей добавили его в свою последнюю версию репозитория Maven.
Скотт Стэнчфилд

5
Если я обновлю jar-файл, в котором классы, которые я использую, совместимы с API-интерфейсом, и я не использую синтаксис импорта по требованию, это никак не повлияет на меня. Это имеет смысл для вас? Не ленитесь при определении импорта, и это не проблема. Синтаксис импорта по требованию был ошибкой в ​​определении языка Java; разумный язык не должен допускать подобных ошибок.
Скотт Стэнчфилд

67

Это не плохо , чтобы использовать джокер с оператором импорта Java.

В чистом коде Роберт С. Мартин фактически рекомендует использовать их, чтобы избежать длинных списков импорта.

Вот рекомендация:

J1: избегайте длинных списков импорта, используя подстановочные знаки

Если вы используете два или более классов из пакета, импортируйте весь пакет с

импортная упаковка. *;

Длинные списки импорта пугают читателя. Мы не хотим загромождать вершины наших модулей с 80 линиями импорта. Скорее мы хотим, чтобы импорт был кратким заявлением о том, с какими пакетами мы сотрудничаем.

Конкретный импорт - это жесткие зависимости, тогда как импорт с подстановочными знаками - нет. Если вы специально импортируете класс, то этот класс должен существовать. Но если вы импортируете пакет с подстановочным знаком, никаких особых классов не должно быть. Оператор импорта просто добавляет пакет в путь поиска при поиске имен. Таким образом, при импорте не создается никакой реальной зависимости, и поэтому они служат для того, чтобы наши модули были менее связанными.

Есть моменты, когда длинный список конкретных импортов может быть полезен. Например, если вы имеете дело с унаследованным кодом и хотите выяснить, для каких классов вам нужно создавать макеты и заглушки, вы можете пройти по списку определенных импортов, чтобы найти истинные квалифицированные имена всех этих классов, а затем поставить соответствующие заглушки на месте. Тем не менее, это использование для конкретного импорта очень редко. Кроме того, большинство современных IDE позволяют вам конвертировать импортируемые символы в список определенных импортов с помощью одной команды. Так что даже в устаревшем случае лучше импортировать подстановочные знаки.

Импорт подстановочных знаков может иногда вызывать конфликты имен и неоднозначности. Два класса с одинаковыми именами, но в разных пакетах, должны быть специально импортированы или, по крайней мере, специально квалифицированы при использовании. Это может быть неприятно, но достаточно редко, когда использование подстановочного импорта все же обычно лучше, чем определенный импорт.


41
Я бы предложил Роберту К. Мартину использовать более совершенные шаблоны для создания более лаконичных пакетов и собственных классов, которые не требуют 80 строк импорта. То, что многие классы, необходимые для импорта внутри одного класса, просто просит «Энтропия, Энтропия, разбей меня, пожалуйста ...» и указывает причину, по которой следует избегать импорта *, обрисованного в общих чертах в ответах Скотта Стэнчфилдса
Рэй

28
Как бы мне ни нравилось то, что говорит дядя Боб, в этом случае мне также приходится не соглашаться с ним.

33
Длинные списки импорта пугают читателя. - Это утверждение имеет неверную презумпцию. Программисты не обязаны читать исходный код сверху вниз. Мы не можем читать списки импорта вообще. Когда мы это сделаем, мы могли бы прочитать только один из импорта, для пояснения. В других случаях импорт может быть полностью свернут, если мы работаем в IDE. Независимо от источника, сегодня это плохой совет.
Энди Томас,

10
Просто чтобы обеспечить некоторый противовес, когда речь идет о цитировании авторитетов по этому вопросу: в Руководстве по стилю Google Java, а также в Руководстве по стилю Java в Твиттере (которое, по большому счету, основано на Google), специально запрещается импорт подстановочных знаков. Но они не дают никакого обоснования этому решению.
другой узел

1
Наверное, единственное, с чем я не согласился в Чистом коде. Нужно прокручивать несколько строк операторов импорта или пытаться найти источник класса. Я предпочитаю легко определять, откуда приходит определенный класс.
Ганеш Сатпут

27

Производительность : не влияет на производительность, так как байт-код одинаков. хотя это приведет к некоторым издержкам компиляции.

Компиляция : на моем персональном компьютере компиляция пустого класса без импорта чего-либо занимает 100 мс, но тот же класс при импорте java. * Занимает 170 мс.


3
import java.*ничего не импортирует. Почему это имеет значение?
маркиз Лорн

6
Это имеет значение, потому что его ищут во время компиляции.
LegendLength

25

Это загромождает ваше пространство имен, требуя от вас полного указания любых двусмысленных имен классов. Наиболее распространенным случаем этого является:

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

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

Это также помогает конкретизировать ваши зависимости, так как все ваши зависимости перечислены вверху файла.


20
  1. Это помогает идентифицировать конфликты имен классов: два класса в разных пакетах с одинаковыми именами. Это может быть замаскировано с помощью * import.
  2. Это делает явные зависимости, так что каждый, кто будет читать ваш код позже, знает, что вы хотели импортировать, а что не хотели импортировать.
  3. Это может ускорить компиляцию, потому что компилятору не нужно искать весь пакет, чтобы выявить зависимости, хотя это обычно не так уж важно для современных компиляторов.
  4. Неудобные аспекты явного импорта сводятся к минимуму с помощью современных сред IDE. Большинство IDE позволяют вам свернуть раздел импорта, чтобы он не мешал, автоматически заполняет импорт при необходимости и автоматически определяет неиспользуемый импорт, чтобы помочь очистить его.

В большинстве мест, где я работал, где используется сколько-нибудь значительное количество Java, явный импорт является частью стандарта кодирования. Иногда я все еще использую * для быстрого создания прототипов, а затем расширяю списки импорта (некоторые IDE будут делать это и для вас) при создании кода.


Мне нравятся большинство твоих замечаний, но именно №4 заставил меня ответить на твой ответ. Современные IDE удаляют большинство аргументов против использования явного импорта ...
Шелдон Р.

Возможно, часть проблемы здесь в том, как стандартные библиотеки Java размещены со многими классами в одном пакете. В отличие от применения большего «принципа единой ответственности» к пакету.
LegendLength

11

Я предпочитаю определенный импорт, потому что он позволяет мне видеть все внешние ссылки, используемые в файле, не просматривая весь файл. (Да, я знаю, что это не обязательно покажет полностью квалифицированные ссылки. Но я избегаю их, когда это возможно.)


9

В предыдущем проекте я обнаружил, что переход от * -импортов к конкретным импортам сокращает время компиляции вдвое (с примерно 10 минут до примерно 5 минут). * -Import заставляет компилятор искать в каждом из перечисленных пакетов класс, соответствующий тому, который вы использовали. Хотя это время может быть небольшим, оно складывается для больших проектов.

Побочным эффектом * -import было то, что разработчики будут копировать и вставлять общие строки импорта, а не думать о том, что им нужно.


11
Чтобы это было правдой, должно быть, было много импортных линий или действительно жалкая система разработки. Я использую import- * и могу собрать всю кодовую базу из 2107 классов менее чем за 2 минуты.
Лоуренс Дол

6

В DDD книге

В какой бы технологии разработки не была основана реализация, ищите способы минимизировать работу по рефакторингу МОДУЛЕЙ. В Java нет возможности избежать импорта в отдельные классы, но вы можете по крайней мере импортировать целые пакеты за раз, отражая намерение, что пакеты являются очень связными единицами, одновременно уменьшая усилия по изменению имен пакетов.

И если он загромождает локальное пространство имен, это не ваша вина - виноват размер пакета.


3
  • Влияния во время выполнения не происходит, так как компилятор автоматически заменяет * конкретными именами классов. Если вы декомпилируете файл .class, вы никогда не увидите import ...*.

  • C # всегда использует * (неявно), так как вы можете только usingимя пакета. Вы никогда не можете указать имя класса вообще. Java вводит эту функцию после c #. (Java очень сложен во многих аспектах, но это выходит за рамки этой темы).

  • В Intellij Idea, когда вы «организуете импорт», он автоматически заменяет несколько импортов одного пакета на *. Это обязательная функция, так как вы не можете отключить ее (хотя вы можете увеличить порог).

  • Случай, указанный в принятом ответе, недействителен. Без * у вас все еще та же проблема. Вам необходимо указать имя pakcage в вашем коде независимо от того, используете вы * или нет.


1
В IntelliJ это не обязательная функция, и ее можно отключить.
Bastien7

3

Для записи: когда вы добавляете импорт, вы также указываете свои зависимости.

Вы могли быстро увидеть, каковы зависимости файлов (исключая классы одного и того же пространства имен).


Согласен. Мотиватором является не столько производительность или компиляция, сколько удобочитаемость вашего кода. Представьте, что вы читаете код без IDE - например, на GitHub. Внезапно поиск каждой ссылки, не определенной в файле, который вы читаете, становится утомительным.
Лев

2

Наиболее важным является то, что импорт java.awt.* может сделать вашу программу несовместимой с будущей версией Java:

Предположим, что у вас есть класс с именем «ABC», вы используете JDK 8 и импортируете java.util.*. Теперь предположим, что выходит Java 9, и у нее есть новый класс в пакете, java.utilкоторый по совпадению также называется «ABC». Ваша программа теперь не будет компилироваться на Java 9, потому что компилятор не знает, подразумеваете ли вы с именем «ABC» свой собственный класс или новый класс вjava.awt .

У вас не будет этой проблемы, если вы импортируете только те классы, которые явно java.awt используются.

Ресурсы:

Импорт Java


3
Совет: вы могли бы использовать Streamв качестве примера новый класс, добавленный в Java в java.util в Java 8 ...
Клинт Иствуд,

2

Среди всех правильных замечаний, высказанных с обеих сторон, я не нашел своей основной причины избегать подстановочного знака: мне нравится иметь возможность читать код и непосредственно знать, что представляет собой каждый класс, или его определение не на языке или файл, где его найти. Если импортировано более одного пакета с *, мне нужно поискать каждый из них, чтобы найти класс, который я не узнаю. Читаемость является высшей, и я согласен, что код не должен требовать IDE для его чтения.


Если вы доведите это до полного логического завершения, тогда ваш стиль должен заключаться в том, чтобы вообще не использовать импорт, а вместо «нового LinkedList» всегда использовать «новый java.util.LinkedList» и делать это последовательно везде .
Эрвин Смут
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.