Короткий ответ
PECS (Producer Extends, Consumer Super) — мнемоническое правило выбора wildcard в параметрах методов: если метод только читает коллекцию («производитель»), объявляйте ? extends T; если только добавляет элементы («потребитель») — ? super T. Правило нужно из-за инвариантности дженериков: List<Cat> не является подтипом List<Animal>.
Как это работает подробнее
«Producer Extends»: метод только читает элементы — коллекция производит данные. Параметр ? extends T задаёт верхнюю границу: Collection<? extends Animal> примет и коллекцию Animal, и коллекцию любого наследника (Cat, Dog). Читать можно как Animal, а добавлять нельзя (кроме null): компилятор не знает фактический тип элементов.
«Consumer Super»: метод только добавляет элементы — коллекция потребляет данные. Параметр ? super T задаёт нижнюю границу: Collection<? super Cat> принимает Collection<Cat>, Collection<Pet> или Collection<Object>. Добавлять можно Cat и его наследников, а при чтении гарантирован только Object.
- Эталон из JDK:
Collections.copyс сигнатурой «приёмник через super, источник через extends». - Неограниченный wildcard
?>означает «любой тип» — он уместен, когда конкретный тип элементов не важен.
Пример кода
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
class Animal {}
class Cat extends Animal {}
public class PecsDemo {
// Producer Extends: коллекцию только читаем
static void feedAll(Collection<? extends Animal> animals) {
for (Animal a : animals) {
System.out.println(a);
}
// animals.add(new Animal()); // не скомпилируется
}
// Consumer Super: в коллекцию только добавляем
static void addCat(Collection<? super Cat> cats) {
cats.add(new Cat());
}
public static void main(String[] args) {
List<Cat> cats = new ArrayList<>();
cats.add(new Cat());
feedAll(cats); // List<Cat> подходит для ? extends Animal
List<Object> objects = new ArrayList<>();
addCat(objects); // List<Object> подходит для ? super Cat
}
}Что использовать на практике
- Проектируя метод, определите направление потока данных: только читаем коллекцию —
extends, только пишем —super. - Копирование коллекций стройте по образцу
Collections.copy. - Когда тип не важен (размер, проверка пустоты), используйте неограниченный wildcard.
Подводные камни
- В
Collection<? extends Animal>нельзя добавить элемент — частый источник ошибок компиляции. - При чтении из
Collection<? super Cat>получите толькоObject— потребуется приведение. - Без wildcard передать список
Catв метод, ожидающий списокAnimal, нельзя: дженерики инвариантны.
Как отвечать на собеседовании
- Каркас: расшифруйте PECS → объясните логику: читаем данные — коллекция «производит» (extends), пишем — «потребляет» (super) → приведите сигнатуру-пример и
Collections.copy→ завершите причиной: инвариантность дженериков и необходимость wildcard.