Ответ
Generics (обобщения) — это механизм Java, позволяющий создавать типобезопасные классы, интерфейсы и методы с параметризованными типами. Тип указывается в угловых скобках: <T>.
Основные преимущества:
- Безопасность типов (Type Safety): Ошибки несоответствия типов обнаруживаются на этапе компиляции, а не во время выполнения.
- Устранение приведения типов (Casts): Код становится чище, без постоянных
(String) list.get(0). - Повторное использование кода: Один алгоритм может работать с разными типами данных.
Без Generics (старый стиль):
List list = new ArrayList(); // Raw type
list.add("hello");
String s = (String) list.get(0); // Требуется явное приведение
Integer i = (Integer) list.get(0); // ClassCastException во время РАБОТЫ!
С Generics:
List<String> list = new ArrayList<>(); // Тип указан
list.add("hello");
String s = list.get(0); // Приведение не нужно
// list.add(123); // ОШИБКА КОМПИЛЯЦИИ - нельзя добавить Integer в List<String>
Создание generic-класса:
public class Box<T> {
private T content;
public void setContent(T content) { this.content = content; }
public T getContent() { return content; }
}
// Использование
Box<String> stringBox = new Box<>();
stringBox.setContent("Text");
Box<Integer> intBox = new Box<>();
intBox.setContent(42);
Wildcards (подстановочные типы):
// 1. Unbounded Wildcard - любой тип
public void printList(List<?> list) {
for (Object elem : list) System.out.println(elem);
}
// 2. Upper Bounded Wildcard - T и его ПОДКЛАССЫ
public double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number n : list) sum += n.doubleValue();
return sum;
}
// Можно передать List<Integer>, List<Double>, но НЕЛЬЗЯ добавлять элементы!
// 3. Lower Bounded Wildcard - T и его НАДКЛАССЫ
public void addNumbers(List<? super Integer> list) {
list.add(1); // Можно добавлять Integer
list.add(2);
}
// Можно передать List<Integer>, List<Number>, List<Object>
Ограничения Generics (Type Erasure):
- Стирание типов: Информация о generic-типах удаляется во время компиляции и недоступна во время выполнения.
// НЕ РАБОТАЕТ во время выполнения: if (list instanceof List<String>) { ... } // Ошибка компиляции T instance = new T(); // Ошибка компиляции T[] array = new T[10]; // Ошибка компиляции - Решение для массивов: Использовать
List<T>вместо массивов или создавать массив через рефлексию (не рекомендуется). - Нельзя использовать примитивы: Только ссылочные типы. Используйте
List<Integer>, а неList<int>.
Best Practices:
- Всегда используйте Generics для коллекций и собственных generic-классов.
- Предпочитайте
List<String>raw typeList. - Используйте
varс Generics для сокращения кода:var list = new ArrayList<String>(); - Для создания generic-массивов используйте обходной путь:
@SuppressWarnings("unchecked") T[] array = (T[]) new Object[size]; - Помните о PECS (Producer Extends, Consumer Super):
- Если коллекция производит элементы (
Tвозвращается) — используйте? extends T. - Если коллекция потребляет элементы (
Tпринимается) — используйте? super T.
- Если коллекция производит элементы (