← Назад к вопросам

В чем разница между 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/ProducerConsumer/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 при работе с полиморфными коллекциями.

В чем разница между extends и super в контексте generics? | PrepBro