← Назад к вопросам
В чем разница между extends и super в контексте generics?
3.0 Senior🔥 61 комментариев
#Коллекции#ООП#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между extends и super в Generics
###概念基础
В контексте обобщённых типов (generics) в Java, extends и super — это wildcard constraints, которые ограничивают тип параметра и определяют его положение в иерархии наследования. Это ключевая часть вариантности типов (type variance).
Extends: Ковариантность (Covariance)
Синтаксис <? extends T> означает "этот тип может быть T или любым подтипом T". Это называется upper bound (верхняя граница).
public void printList(List<? extends Number> list) {
for (Number num : list) {
System.out.println(num);
}
// list.add(5); // ОШИБКА: не можем добавлять
}
// Можем передать List<Integer>, List<Double>, List<Number>
List<Integer> integers = Arrays.asList(1, 2, 3);
printList(integers); // OK
List<Double> doubles = Arrays.asList(1.5, 2.5);
printList(doubles); // OK
Принцип PECS (Producer Extends Consumer Super):
- Используй
extendsкогда читаешь из коллекции (producer) - При
<? extends T>можем только читать элементы как T - Не можем добавлять элементы (кроме null), потому что точный тип неизвестен
Super: Контравариантность (Contravariance)
Синтаксис <? super T> означает "этот тип может быть T или любым родительским типом T". Это lower bound (нижняя граница).
public void fillList(List<? super Integer> list) {
list.add(5); // OK: добавляем Integer
list.add(10);
// Number num = list.get(0); // ОШИБКА: неизвестный тип
}
// Можем передать List<Integer>, List<Number>, List<Object>
List<Integer> integers = new ArrayList<>();
fillList(integers); // OK
List<Number> numbers = new ArrayList<>();
fillList(numbers); // OK
Ключевые моменты:
- Используй
superкогда пишешь в коллекцию (consumer) - При
<? super T>можем добавлять элементы типа T и подтипов - Можем только читать как Object, потому что конкретный родительский тип неизвестен
Сравнение в таблице
| Аспект | <? extends T> | <? super T> |
|---|---|---|
| Граница | Upper bound (верхняя) | Lower bound (нижняя) |
| Иерархия | T и его подтипы | T и его родительские типы |
| Чтение | ✅ Как T | ⚠️ Как Object |
| Запись | ❌ Невозможна | ✅ Как T |
| Использование | Source/Producer | Consumer/Sink |
| Пример | List<? extends Number> | List<? super Integer> |
Практический пример
public class CopyUtils {
// Копируем ИЗ source (читаем) — используем extends
// Копируем В destination (пишем) — используем super
public static <T> void copy(
List<? extends T> source,
List<? super T> destination) {
for (T element : source) {
destination.add(element);
}
}
}
// Использование
List<Integer> integers = Arrays.asList(1, 2, 3);
List<Number> numbers = new ArrayList<>();
CopyUtils.copy(integers, numbers); // OK: Integer extends Number
Правило PECS в деталях
- Producer (Производитель): если получаешь данные из generic параметра →
<? extends T> - Consumer (Потребитель): если добавляешь данные в generic параметр →
<? super T> - Когда обе операции: используй без wildcards →
<T>
Это один из самых важных и сложных аспектов Java generics, который обеспечивает type-safety при работе с полиморфными коллекциями.