Короткий ответ
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
}
}