Чому неправильне використання підказки із заявою про імпорт 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
Це перший сценарій, який відбудеться. Компілятор помічає, що є два класи подій, і видає помилку.
jan.vdbergh

38
Не забудьте перевірити мій коментар нижче - виникає більша проблема із типом, що додаються до сторонніх контурів із часом. Ви можете мати компілюючий код, який припиняє компілювати після того, як хтось додає тип в банку, від якої ви залежите.
Скотт Стенчфілд

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

1
Ви можете вирішити подібні конфлікти, не чітко перераховуючи кожен клас, що спричиняє власні проблеми.
rpjohnst

196

Ось голосування за імпорт зірок. Заява про імпорт призначена для імпорту пакету , а не класу. Набагато чистіше імпортувати цілі пакети; виявлені проблеми тут (наприклад , java.sql.Dateпроти java.util.Date) легко виправити з допомогою інших засобів, а НЕ на насправді вирішувати за рахунок імпорту конкретних і , безумовно , не виправдовують душевнохворого педантичних імпорту по всіх класах. Немає нічого більш неприємного, ніж відкрити вихідний файл і перегортати 100 заяв про імпорт.

Здійснення конкретного імпорту ускладнює рефакторинг; якщо ви видалите / перейменуєте клас, вам потрібно видалити весь його конкретний імпорт. Якщо ви переключите реалізацію на інший клас в одному пакеті, вам доведеться виправити імпорт. Хоча ці додаткові кроки можна автоматизувати, вони справді досягають продуктивності без реального виграшу.

Навіть якби Eclipse не робив імпорт класів за замовчуванням, все одно б імпортували зірки. Вибачте, але дійсно немає раціонального обґрунтування здійснення конкретного імпорту.

Ось як поводитися з класовими конфліктами:

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

28
Я згоден. Хоча я б не проти використовувати явний імпорт, я все ж вважаю за краще використовувати імпорт зірок. Вони підкреслюють, що "одиниця повторного використання" - це весь пакет, а не його окремі типи. Причини, які інші перераховують проти імпорту зірок, слабкі, і на мій досвід використання імпорту зірок ніколи не викликав жодних фактичних труднощів.
Rogério

32
Дивіться javadude.com/articles/importondemandisevil.html для детальної інформації, чому це зло. Основна ідея: це може призвести до припинення компіляції коду, коли класи додаються до пакетів, які ви імпортуєте (наприклад, коли список додано до 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 додала в java.util інтерфейс з назвою List. БУМ! Конфлікт. Ідеально працюючий код більше не працює.

Це особливість мови EVIL . Там немає НІЯКОЇ причини того, що код повинен припинити збір тільки тому тип додається в пакет ...

Крім того, читачеві важко визначити, який саме "Foo" ви використовуєте.


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

36
@PabloFernandez - Nope - Якщо я перевіряю код, який знаходиться у сховищі протягом року, він все одно повинен компілюватися. Імпорт за запитом може легко вийти з ладу, коли до існуючих пакетів, які я імпортував , додаються нові класи . Це не лише проблема при модернізації версій Java. Також - якщо API розроблений добре, він ніколи не повинен порушувати існуючий код при оновленні. Єдиний раз, коли мені потрібно було змінити код під час оновлення версій Java, це було через імпорт на вимогу і коли Sun втягував API XML у режим Java.
Скотт Стенчфілд

3
Додавання класу (з унікальним, повнокваліфікованим іменем!) На classpath не повинно нічого впливати. Суть у тому, що якщо ви не використовуєте синтаксис імпорту на вимогу, він не буде. Тому не використовуйте поганий синтаксис, який дозволяє мова, на жаль, і це одна менш реальна проблема, з якою ви можете потрапити.
Скотт Стенчфілд

28
Суть моєї відповіді полягає в тому, що це непотрібна мовна функція, яка викликає проблеми. Багато IDE / редакторів автоматично обробляють розширення імпорту. Використовуйте повнокваліфікований імпорт, і немає шансів, що ця конкретна помилка станеться. На мене це потрапило під тиском, щоб виправити помилку в існуючому коді, і вам справді не потрібно щось подібне, як відволікання від справжнього завдання. java.util.Listvs java.awt.Listне надто погано, щоб розібратися, але спробуйте, коли ім'я класу є, Configurationі кілька бібліотек залежностей додали його в останню версію Maven Repo.
Скотт Стенчфілд

5
Якщо я оновлюю банку, де класи, які я використовую, сумісні з переадресацією API, і я не використовую синтаксис імпорту на вимогу, це зовсім не вплине на мене. Це має для вас сенс? Не полінуйтеся визначити імпорт, і це не питання. Синтаксис "імпорт на запит" був помилкою у визначенні мови Java; розумна мова не повинна допускати подібних помилок.
Скотт Стенчфілд

67

Це НЕ погано , щоб використовувати джокер з оператором імпорту Java.

У чистому коді Роберт К. Мартін фактично рекомендує використовувати їх, щоб уникнути довгих списків імпорту.

Ось рекомендація:

J1: уникайте довгих імпортних списків, використовуючи символи

Якщо ви використовуєте два або більше класів з пакету, то імпортуйте весь пакет за допомогою

імпортний пакет. *;

Довгі переліки імпорту неприємні для читача. Ми не хочемо захаращувати вершини наших модулів 80 лініями імпорту. Швидше ми хочемо, щоб імпорт був стислим твердженням про те, з якими пакетами ми співпрацюємо.

Конкретний імпорт є важкою залежністю, тоді як імпорт wildcard - ні. Якщо ви спеціально імпортуєте клас, він повинен існувати. Але якщо ви імпортуєте пакет із символом підстановки, особливих класів не потрібно існувати. Заява про імпорт просто додає пакунок до шляху пошуку під час пошуку імен. Таким чином, жодна справжня залежність від такого імпорту не створюється, і тому вони служать для того, щоб наші модулі були менш зв'язаними.

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

Імпорт шаблонів іноді може викликати конфлікти і неоднозначності імен. Два класи з однаковою назвою, але в різних упаковках, потрібно буде спеціально імпортувати або хоча б спеціально кваліфікувати при їх використанні. Це може спричинити занепокоєння, але досить рідко, що використання імпортних символів все ще краще, ніж конкретний імпорт.


41
Я б запропонував Роберту К. Мартіну використовувати кращі зразки для створення більш коротких власних пакетів та класів, які не потребують 80 ліній імпорту. Що багато класів, необхідних для імпорту всередині одного класу, просто благають "Entropy, Entropy, break me please ..." і вказує на причину уникнення імпорту *, викладеного в альтах Скотта Стенчфілда
Рей

28
Наскільки я, як правило, люблю те, що має сказати дядько Боб, в цьому випадку я також повинен погодитися з ним.

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

10
Просто щоб надати певну противагу, коли йдеться про посилання на владні повноваження щодо цього питання: Посібник із стилів Google по Java , а також посібник із стилів Java щодо Twitter (який, в основному, базується на 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. Це допомагає виявити конфлікти імен класу: два класи в різних пакетах, які мають однакове ім'я. Це можна замаскувати імпортом *.
  2. Це робить залежності явними, тому кожен, хто має згодом прочитати ваш код, знає, що ви мали намір імпортувати і що ви не хотіли імпортувати.
  3. Це може зробити компіляцію швидше, оскільки компілятору не потрібно шукати весь пакет, щоб виявити особи, хоча це, як правило, не є великою справою із сучасними компіляторами.
  4. Незручні аспекти явного імпорту мінімізовані за допомогою сучасних ІДЕ. Більшість IDE дозволяють згортати розділ імпорту, тому це не заважає, автоматично заповнювати імпорт за потреби та автоматично визначати невикористаний імпорт, щоб допомогти їх очистити.

Більшість місць, де я працював, які використовують будь-яку значну кількість Java, явно імпортують частину стандарту кодування. Я інколи все ще використовую * для швидкого прототипування, а потім розширюю списки імпорту (деякі IDE будуть робити це і для вас), коли виробляють код.


Мені подобається більшість ваших пунктів, але саме 4 змусило мене схвалити вашу відповідь. Сучасні ІДЕ усувають більшість аргументів проти використання явного імпорту ...
Шелдон Р.

Можливо, частина проблеми тут полягає в тому, як стандартні бібліотеки Java розміщені з багатьма класами в одному пакеті. На відміну від застосування більшої кількості "єдиного принципу відповідальності" до пакету.
LegendLength

11

Я віддаю перевагу конкретному імпорту, тому що він дозволяє мені бачити всі зовнішні посилання, що використовуються у файлі, не дивлячись на весь файл. (Так, я знаю, що це не обов'язково містить повністю кваліфіковані посилання. Але я уникаю їх, коли це можливо.)


9

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

Побічним ефектом * -імпорту було те, що розробники копіювали та вставляли загальні лінії імпорту, а не думали про те, що їм потрібно.


11
Повинне було бути багато ліній імпорту або дуже жалюгідна система розвитку, щоб це було правдою. Я використовую import- * a Я можу скласти всю свою кодову базу з 2107 класів за 2 хвилини.
Лоуренс Дол

6

У книзі DDD

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

І якщо він захаращує місцевий простір імен, це не ваша вина - звинувачуйте розмір пакету.


3
  • Немає впливу на час виконання, оскільки компілятор автоматично замінює * конкретними іменами класів. Якщо ви декомпілюєте файл .class, ви ніколи не побачите import ...*.

  • C # завжди використовує * (неявно), оскільки ви можете тільки usingназву пакета. Ніколи не можна вказувати назву класу. Java вводить функцію після c #. (Ява настільки хитра в багатьох аспектах, але це поза цією темою).

  • У Intellij Idea, коли ви "організовуєте імпорт", він автоматично замінює багаторазовий імпорт одного пакету на *. Це обов'язкова функція, оскільки ви не можете її вимкнути (хоча ви можете збільшити поріг).

  • Справа, перелічена прийнятою відповіддю, недійсна. Без * у вас все-таки вийшов той самий випуск. Вам потрібно вказати ім'я пакунка у коді, незалежно від того, використовуєте ви * чи ні.


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.