Использование wildcard imports в Java. Хорошо или плохо?

В этой заметке мы обсудим преимущества и недостатки использования импорта wildcard Import в Java.

· 4 минуты на чтение

Разработчики Java используют подстановочный знак (*) в операторах import для добавления всех классов из определенного пакета. Но в ходе ревью большинство из вас, возможно, просили убрать этот подстановочный знак импорта и добавить полное имя класса. Разберемся почему не стоит использовать знак подстановки?

Но прежде, давайте вспомним, что полное имя класса, помимо названия, содержит также пакет, например: java.util.List. Такая запись позволяет нам иметь классы с одинаковыми именами, которые находятся в разных пакетах.

package com.example;

public class WithoutImport {

    public static void main(String[] args) {

        java.util.List myList = new java.util.ArrayList();

    }
}

Однако, полная запись весьма непрактична, поэтому в Java существует оператор импорта – import. Операторы импорта объявляют компилятору источник имен классов, статических переменных и статических имен методов, используемых в коде. Один раз написали полное имя класса, а дальше используем только название класса.

package com.example;

import java.util.ArrayList;
import java.util.List;

public class WithImport {

    public static void main(String[] args) {

       List myList = new ArrayList();

    }

}
Пример с использованием оператора import

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

Импорт с подстановочными знаками указывает компилятору искать имена классов в данном пакете. Следовательно, при использовании импорта с wildcard производительность во время компиляции может немного снизиться. Но в большинстве случаев это не окажет заметного влияния.

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

package com.example;

import java.util.*;

public class WithWildCard {
    public static void main(String[] args) {

        List myList = new ArrayList();

    }
}

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

Compiled from "WithWildCard.java"
public class com.example.WithWildCard {
  public com.example.WithWildCard();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: return
}

Если сравнить его с байт-кодом прошлого примера, то они оба имеют одинаковый байт-код.

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

Спонсор поста

Недостатки

Так если использование подстановочного знака не приводит к проблемам производительности, то почему бы его не использовать?

Конфликты имен

Самая серьезная проблема это возможные конфликты именования. Представьте, что у нас есть два класса из разных библиотек org.test.Parser и dev.lib.Parser. В нашем коде мы используем импорт с wildcard:

import org.test.*
import dev.lib.*

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

Также проблема может возникнуть в будущем, когда вы обновите версию какую-то из библиотек. Допустим у нас была библиотека с классом org.test.Parser, а потом разработчик второй библиотеки тоже добавил класс dev.lib.Parser, таким образом мы получили конфлик именования в будущем, хотя раньше все было нормально.

Чистый код

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

Эта идея также поддерживается в книге "Чистый код" Роберта К. Мартина. Фактически, книга рекомендует использовать импорты с подстановочными знаками при использовании нескольких классов из одного источника. Другими словами, когда мы импортируем два или более классов, импортированных из пакета, лучше импортировать весь пакет.

Однако, я не считаю это какой-то проблемой, так как IntelliJ IDEA автоматически скрывает все строки импортов.

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

Однако, это тоже не является проблемой. Достаточно удерживая Ctrl (cmd) навести курсор на класс и вы увидите пакет, в котором этот класс размещается.

Таким образом проблемы "чистого кода" более не актуальны.

Настройки в Idea

Intellij Idea автоматически сворачивает импорты, когда полные импорты из какого-то пакета достигают заданного количества. Эта опция настраивается в раделе Preferences > Editor > Code Style > Java во вкладке imports.

Чтобы отключить автоматическое сворачивание импортов достаточно указать число 999. Вот и все, теперь Idea не будет автоматически сворачивать импорты.

Резюмирую

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

Также при обновлении библиотек вы можете столкнуться с проблемой конфликта имен. Шанс этой проблемы не велик, но и не нулевой.

Что касается "чистого кода", то с использованием современных IDE эта проблема не является актуальной.

Struchkov Mark
Struchkov Mark
Задавайте вопросы, если что-то осталось не понятным👇