Объясни принцип PECS (Producer Extends, Consumer Super) в Java Generics и приведи пример.

«Объясни принцип PECS (Producer Extends, Consumer Super) в Java Generics и приведи пример.» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

PECS (Producer Extends, Consumer Super) — это мнемоническое правило для безопасного использования подстановочных типов (wildcards) ? extends и ? super в Java Generics. Оно определяет, какую операцию (чтение или запись) можно безопасно выполнять с коллекцией.

1. Producer Extends (? extends T) Используется, когда коллекция производит (отдает) элементы типа T. С такой коллекцией можно безопасно читать, но нельзя добавлять новые элементы (кроме null).

// Коллекция — производитель Number
List<? extends Number> numbers = Arrays.asList(1, 2.5, 3L);

// БЕЗОПАСНО: Чтение. Все элементы можно трактовать как Number.
Number first = numbers.get(0);
Double second = (Double) numbers.get(1); // После проверки
for (Number n : numbers) { /* ... */ }

// НЕБЕЗОПАСНО / ОШИБКА КОМПИЛЯЦИИ: Запись.
// numbers.add(10);   // Нельзя добавить Integer
// numbers.add(3.14); // Нельзя добавить Double
// Компилятор не знает точный тип внутри List.

2. Consumer Super (? super T) Используется, когда коллекция потребляет (принимает) элементы типа T. В такую коллекцию можно безопасно записывать объекты T (и его подтипы), но читать можно только как Object.

// Коллекция — потребитель Integer
List<? super Integer> integers = new ArrayList<Number>();

// БЕЗОПАСНО: Запись. Можно добавлять Integer и его подтипы.
integers.add(42);
integers.add(100);

// НЕБЕЗОПАСНО: Чтение с конкретным типом.
// Integer i = integers.get(0); // Ошибка компиляции
// Компилятор знает только, что это некий супертип Integer.

// БЕЗОПАСНО: Чтение как Object.
Object obj = integers.get(0);

Итог:

  • Для чтения используй ? extends T (коллекция — производитель).
  • Для записи используй ? super T (коллекция — потребитель). Это правило критически важно для написания типобезопасных обобщенных методов, таких как Collections.copy().