Ответ
PECS (Producer Extends, Consumer Super) — это мнемоническое правило для безопасного использования подстановочных знаков (wildcards) ? extends и ? super в дженериках Java. Оно определяет, какую вариативность типов использовать для коллекций, которые только производят (читают) или только потребляют (записывают) элементы.
-
Producer Extends (
? extends T): Используй, когда коллекция производит (отдаёт) элементы типаT. Элементы можно безопасно читать какT.// Метод только читает числа из списка (Producer) double sum(List<? extends Number> numbers) { double total = 0; for (Number n : numbers) { // Безопасное чтение total += n.doubleValue(); } return total; } // Можно вызвать: sum(List<Integer>), sum(List<Double>) -
Consumer Super (
? super T): Используй, когда коллекция потребляет (принимает) элементы типаT. В неё можно безопасно записывать объектыTи его подтипов.// Метод только записывает целые числа в список (Consumer) void addIntegers(List<? super Integer> list) { list.add(42); // Безопасная запись Integer list.add(Integer.valueOf(100)); } // Можно вызвать: addIntegers(List<Integer>), addIntegers(List<Number>), addIntegers(List<Object>)
Ключевое правило:
- Чтение (get) ->
extends - Запись (add) ->
super - И то, и другое -> не используй wildcard (просто
<T>)
PECS повышает гибкость API, позволяя методам работать с более широким диапазоном типов, сохраняя типобезопасность.
Ответ 18+ 🔞
О, слушай, смотри, сейчас объясню про эту вашу PECS, а то как будто в Толстого какого-то вляпались, все эти «extends» да «super». Запоминай раз и навсегда, а то потом будешь как Герасим, только «Му-му» говорить и в озеро коллекции кидать.
Представь, у тесть есть коробка. Коробка дженерик-коллекция, окей? Всё сводится к одному: ты из неё только достаёшь или ты в неё только кладёшь?
Первый случай: ты — добытчик, продюсер, производитель (Producer).
Твоя коробка ПРОИЗВОДИТ для тебя элементы. Ты из неё только читаешь, как из библии, блядь. Тебе похуй, что конкретно там лежит — Integer, Double, BigDecimal размером с хуй с горы. Главное, что это всё — числа, потомки Number. Значит, ты объявляешь: «Дайте мне список, из которого я буду ТОЛЬКО ЧИТАТЬ числа!».
И пишешь: List<? extends Number>. Это как сказать: «Эй, Java, расслабься, я ничего твоей коллекции плохого не сделаю, я только посмотрю». И ты можешь безопасно всё это прочитать и обработать.
// Считаю бабки, а там хоть рубли, хоть биткоины (лишь бы Number)
double посчитатьИтог(List<? extends Number> числа) {
double итог = 0;
for (Number n : числа) { // Всё ок, любое Number можно взять
итог += n.doubleValue();
}
return итог;
}
// И передашь ты сюда List<Integer> — ок. List<Double> — ок. List<Number> — тем более ок.
Второй случай: ты — потребитель, жлоб, консьюмер (Consumer).
Твоя коробка ПОТРЕБЛЯЕТ то, что ты в неё суёшь. Ты её наполняешь, как тот мудак из анекдота, который всё в себя тащит. Ты точно знаешь, что ты кладёшь — например, Integer. Но твоя коробка может быть шире: List<Integer>, List<Number>, да хоть List<Object> (всё равно, в рот меня чих-пых!). Главное, чтобы в неё можно было безопасно положить твой Integer.
Значит, ты объявляешь: «Дайте мне список, в который я буду ТОЛЬКО ПИСАТЬ целые числа!».
И пишешь: List<? super Integer>. Это как сказать: «Эй, Java, не бзди, я буду класть только Integer и его детей, а твоя коллекция это переварит, она же шире или такая же».
// Наполняю ящик запасами водки (только Integer)
void наполнитьЯщик(List<? super Integer> ящик) {
ящик.add(42); // Безопасно! Integer влезет в Integer, Number или Object
ящик.add(100);
// ящик.add(new Object()); // А вот это уже НИЗЯ! Потому что ? super Integer — это НЕ Object.
}
// И вызовешь ты это для List<Integer> — ок. Для List<Number> — ок. Для List<Object> — тоже ок, Object всех проглотит.
А теперь главное правило, которое в голове застрянет, как матерное слово:
- Если метод ТОЛЬКО ДОСТАЁТ (GET) из коллекции -> используй
extends(Producer Extends). - Если метод ТОЛЬКО КЛАДЁТ (ADD) в коллекцию -> используй
super(Consumer Super). - Если метод делает И ТО, И ДРУГОЕ -> тогда, чувак, НЕ ЮЗАЙ ВАЙЛДКАРДЫ ВООБЩЕ (
?). Бери нормальный, конкретный тип<T>и не морочь всем голову.
Вот и вся философия. PECS — это не про сложность, а про безопасность и гибкость. Чтобы твой метод мог работать с кучей разных типов, но при этом компилятор не орал как сумасшедший, что ты пытаешься впихнуть Double туда, где ждут только Integer. Ёпта, звучит логично, да? А то смотришь на код некоторых — удивление пиздец, будто они пишут, закрыв глаза.