Producer Extends, Consumer Super — формулировку помнят почти все, кто готовился по generics. Проблема начинается, когда просят написать конкретную сигнатуру метода.

Если метод читает элементы из списка — этот список производитель, Producer, значит extends. Если метод пишет элементы в список — список потребитель, Consumer, значит super.

static void copy( List super T dst, List extends T src) { for (T item : src) { dst.add(item); } }

Здесь src — производитель, из него читают, поэтому extends T. dst — потребитель, в него пишут, поэтому super T. Перепутать местами легко, потому что оба параметра похожи по структуре, а разница именно в направлении данных.

Если поставить extends на dst, компилятор перестанет разрешать add() — тип внутри списка становится неопределённым сверху, JVM не может гарантировать безопасность записи.

Дальше спросят: почему List вообще не даёт добавлять элементы, кроме null. Компилятор знает только верхнюю границу типа — реальный тип внутри списка может быть любым подклассом T, и добавить туда что-то конкретное безопасно нельзя, JVM не проверит совместимость в runtime. Отсюда вытекает вопрос про type erasure: почему эта проверка вообще существует только на этапе компиляции, а не в байткоде.

PECS — не мнемоника для зубрёжки, а прямое следствие того, что происходит с типом при чтении и записи. Если держать в голове направление данных, а не буквы, сигнатура пишется сама.

Тренажёр: 600 вопросов, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки