Приведи пример использования Wildcards и принципа PECS в Java Generics
Что проверяют
Понимаете ли вы, что дженерики в Java инвариантны, и умеете ли писать из-за этого правильные сигнатуры. PECS спрашивают не ради аббревиатуры: смотрят, объясните ли вы, почему в одну коллекцию можно класть, а из другой только читать.
Как отвечать
List<Integer> не является подтипом List<Number>, хотя Integer наследник Number. Иначе через ссылку типа List<Number> можно было бы положить Double в список целых. Wildcards возвращают гибкость ценой ограничения операций:
? extends T: источник. Читать можно, элемент придёт какT; добавить нельзя ничего, кромеnull, потому что конкретный подтип неизвестен;? super T: приёмник. КластьTи его наследников можно, а читать получится только какObject;?без границы: остаются операции, не зависящие от типа, вроде размера и очистки.
Отсюда и правило PECS, Producer Extends Consumer Super, с каноническим примером:
static <T> void copy(List<? super T> dst, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) dst.set(i, src.get(i));
}src производит значения, dst их потребляет, поэтому такая сигнатура принимает и copy(numbers, integers). Тот же приём по всей стандартной библиотеке: Collections.max(Collection<? extends T>), Comparator<? super T> у сортировки, Stream.map(Function<? super T, ? extends R>).
Практический вывод: параметру, который только отдаёт данные, ставим extends, тому, который только принимает, super, а если параметр и читают, и пишут, wildcard не подходит, нужен точный тип.
Где спотыкаются
Пробуют добавить элемент в List<? extends Number> и объявляют это странностью компилятора. За таким типом может стоять List<Double>, и запрет как раз спасает от порчи чужой коллекции.
Применяют PECS к возвращаемым типам и полям. Правило про параметры метода: wildcard в возвращаемом типе протекает наружу и заставляет каждого вызывающего тащить его дальше по коду.
Путают wildcard с параметром типа. Если тип встречается в сигнатуре один раз, хватит ?; если он связывает несколько параметров или параметр с результатом, нужен именно <T>, иначе компилятор не докажет, что это один и тот же тип.