Каковы преимущества и недостатки Generics (обобщений) в Java?

«Каковы преимущества и недостатки Generics (обобщений) в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Преимущества:

  1. Типобезопасность на этапе компиляции: Компилятор проверяет корректность использования типов, предотвращая ошибки ClassCastException во время выполнения.
  2. Устранение приведения типов: Код становится чище, так как исчезает необходимость в явных кастах ((String) list.get(0)).
  3. Повторное использование кода: Можно написать один обобщенный алгоритм (например, Collections.sort()), который будет работать с разными типами.
  4. Более четкий и выразительный код: Объявления вроде List<String> сразу сообщают о предназначении коллекции.

Недостатки и ограничения:

  1. Стирание типов (Type Erasure): Информация об обобщенных типах удаляется компилятором и недоступна во время выполнения. Это ограничивает возможности рефлексии и приводит к известным ограничениям (например, нельзя создать new T()).
  2. Сложность для новичков: Некоторые концепции, особенно подстановочные знаки (wildcards) с ограничениями (<? extends T>, <? super T>), могут быть трудны для понимания.
  3. Невозможность использования с примитивами: Параметрами типов могут быть только ссылочные типы. Для примитивов необходимо использовать классы-обертки (Integer, Double), что может вести к автоупаковке и дополнительным затратам памяти.
  4. Усложнение объявлений: Сигнатуры методов и классов могут стать громоздкими.

Пример типобезопасности:

// БЕЗ Generics: риск ошибки во время выполнения
List list = new ArrayList();
list.add("hello");
Integer num = (Integer) list.get(0); // ClassCastException!

// С Generics: ошибка перехватывается на этапе компиляции
List<String> safeList = new ArrayList<>();
safeList.add("hello");
// Integer num = safeList.get(0); // ОШИБКА КОМПИЛЯЦИИ: несовместимые типы
String str = safeList.get(0); // Корректно, приведение не требуется