Дженерики #
1. Что такое дженерики? Для чего нужны? #
Дженерики - это параметризованные типы в Java
- Что это? Позволяют создавать классы, интерфейсы и методы с типами, заданными как параметры
- Зачем нужны? Позволяют использовать один класс для разных типов данных (
List<String>,List<Integer>)
Набор рекомендаций от Oracle о том, когда какие обозначения лучше использовать в дженериках:
- E - element, для элементов параметризованных коллекций;
- K - key, для ключей map-структур;
- V - value, для значений map-структур;
- N - number, для чисел;
- T - type, для обозначения типа параметра в произвольных классах;
- S, U, V и так далее - применяются, когда в дженерик-классе несколько параметров.
2. Что такое сырые типы? #
Сырые типы - это обобщённые классы без указания типа (List без <>)
3. Что такое вайлдкард #
Вайлдкард (символ ?) используется для обозначения неопределённого типа в дженериках
4. Расскажите про принцип PECS #
Producer Extends, Consumer - Super:
- если мы объявили wildcard с
extends, то это producer. Он только «продюсирует», предоставляет элемент из контейнера, а сам ничего не принимает - если же мы объявили wildcard с
super, то это consumer. Он только принимает, а предоставить ничего не может
Для совмещения обеих возможностей необходимо использовать конкретный тип, а не wildcard.

Альтернативным вариантом является применение паттерна wildcard capture.
На примере ниже для реверсирования коллекции с wildcard мы создаем обобщенный метод rev. Он объявлен с переменной типа T. Этот метод принимает список типов T, и мы можем сделать сет. При вызове метода reverse(List<?> list) в качестве аргумента передается список каких-то объектов (например, строк или целых чисел). Если мы можем захватить тип этих объектов и присвоить его переменной типа X, то можем заключить, что T является X.
public static void reverse(List<?> list) {
rev(list);
}
private static <T> void rev(List<T> list) {
List<T> tmp = new ArrayList<>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size()-i-1));
}
}
5. Расскажите про класс Optional #
Optional - класс-обертка. Он помогает избежать ошибок, связанных с null
Что делает Optional.flatMap()?
Optional.flatMap() используется для работы с вложенными объектами Optional, помогая избежать двойной обёртки, т.е. убирает вложенность: превращает Optional<Optional<T>> в Optional<T>
Optional<String> getNestedValue(Optional<Optional<String>> nestedOpt) {
return nestedOpt.flatMap(opt -> opt);
}
Использовать map, если функция возвращает нужный вам объект, или flatMap, если функция возвращает Optional объект
6. Что такое стирание типов (Type Erasure) в Java Generics #
JVM не хранит информацию о параметризованных типах в рантайме. Все операции с дженериками преобразуются в операции с “сырыми типами” (raw types) на этапе компиляции (стирание типов)
Что такое стирание типов? Стирание типов (Type Erasure) - процесс, при котором информация о дженериках удаляется во время компиляции. JVM видит только “сырой” тип
Generics. Можно ли получить тип в рантайме? В общем случае нет. В Java используется стирание типов (Type Erasure), что означает, что информация о параметризованных типах удаляется после компиляции. На этапе выполнения JVM работает только с “сырыми типами”
Исключение: Можно сохранить информацию о типах с помощью рефлексии или через использование классов-обёрток
Пример с TypeToken:
import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;
Type type = new TypeToken<List<String>>(){}.getType();
System.out.println(type); // java.util.List<java.lang.String>
Табличка терминов по теме
| Термин | Расшифровка |
|---|---|
| Дженерик-типы (generic types) | Дженерик-класс или дженерик-интерфейс с одним или несколькими параметрами в заголовке |
| Параметризованный тип (parameterized types) | Вызов дженерик-типа. Для дженерик-типа List<E> параметризованным типом будет, например, List<Integer> |
| Параметр типа (type parameter) | Используются при объявлении дженерик-типов. Для List<E> E - это параметр типа |
| Аргумент типа (type argument) | Тип объекта, который может использоваться вместо параметра типа. Например, для List<Integer> Integer - это аргумент типа |
| Wildcard | Обозначается символом «?» - неизвестный тип |
| Ограниченный wildcard (bounded wildcard) | Ограниченный сверху <? extends Citrus> снизу - <? super Orange> |
| Сырой тип (raw type) | Имя дженерик-типа без аргументов типа. Для List<E> сырой тип - это List |
| Универсальный параметр | <T>, <E> и т.д. в объявлении классов, интерфейсов, методов |