Gymterview
junior

Что такое generics?

Generics - это технический термин, обозначающий набор свойств языка позволяющих определять и использовать обобщенные типы и методы. Обобщенные типы или методы отличаются от обычных тем, что имеют типизированные параметры.

Примером использования обобщенных типов может служить Java Collection Framework. Так, класс LinkedList<E> - типичный обобщенный тип. Он содержит параметр E, который представляет тип элементов, которые будут храниться в коллекции. Создание объектов обобщенных типов происходит посредством замены параметризированных типов реальными типами данных. Вместо того, чтобы просто использовать LinkedList, ничего не говоря о типе элемента в списке, предлагается использовать точное указание типа LinkedList<String>, LinkedList<Integer> и т.п.

Wildcards (подстановочные символы)

В generics используются подстановочные символы (wildcards):

  • ? — неограниченный подстановочный тип (unbounded wildcard), например List<?> — список элементов неизвестного типа;
  • ? extends T — верхняя граница (upper bounded wildcard), означает «T или любой его подтип». Например, List<? extends Number> может содержать Integer, Double и т.д.;
  • ? super T — нижняя граница (lower bounded wildcard), означает «T или любой его супертип». Например, List<? super Integer> допускает Integer, Number, Object.

Принцип выбора между extends и super описывается правилом PECS (Producer Extends, Consumer Super): если параметризованный тип является источником данных (producer), используйте ? extends T; если потребителем данных (consumer) — ? super T.

Стирание типов (Type Erasure)

Информация о параметрах типов существует только на этапе компиляции. Во время выполнения (runtime) она стирается — это называется стирание типов (type erasure). Компилятор заменяет все параметры типов их границами или Object, если границы не указаны, и при необходимости вставляет приведения типов. Таким образом, List<String> и List<Integer> на уровне байт-кода являются одним и тем же типом List.

Обобщённые методы (Generic Methods)

Обобщённые методы позволяют параметризировать конкретный метод, а не весь класс:

Пример
public static <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;
}

// Вызов:
String result = max("abc", "xyz"); // тип T выводится автоматически

Ограничения generics

  • Нельзя использовать примитивные типы в качестве параметра типа (List<int> — ошибка компиляции, следует использовать List<Integer>);
  • Нельзя создавать экземпляры параметров типа (new T() — ошибка компиляции);
  • Нельзя создавать массивы параметризованных типов (new List<String>[10] — ошибка компиляции);
  • Из-за стирания типов нельзя использовать instanceof с параметризованными типами (obj instanceof List<String> — ошибка компиляции, но obj instanceof List<?> допустимо);
  • Нельзя создавать обобщённые исключения (класс, расширяющий Throwable, не может быть параметризован).