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 — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки