Что было бы если компилятор не следовал правилам PECS?
Предположим, что компилятор пропустит вот такой код
public List<T> filterSportsmen(Predicate<? extends T> predicate) {
List<T> result = new ArrayList<>();
for (T sportsman : sportsmen) {
if (predicate.test(sportsman)) {
result.add(sportsman);
}
}
return result;
}
public void addSportsman(Supplier<? super T> supplier){
sportsmen.add(supplier.get());
}
Какие из этого проблемы последуют? И почему компилятор следует правилу PECS?
Ответы (1 шт):
Если бы компилятор это позволил, то возникли бы проблемы. Вот для примера с consumer-ом, допустим имеем такую иерархию:
class Athlet {
public getName() { return ... }
}
class Thrower extends Athlet {
}
class Runner extends Athlet {
int speed;
public int getSpeed() { return speed; }
}
И предикат:
class IsQuickEnough implements Predicate<Runner> {
@Override
public boolean test(Runner t) {
return t.getSpeed() > 100;
}
}
Тогда возникли бы проблемы с таким кодом (тут я предполагаю, что filterSportsmen определена в классе Sportsmen):
Sportsmen<Athlet> athlets = new Sportsmen<>();
athlets.add(new Thrower());
athlets.filterSportsmen(new IsQuickEnough());
Проблема возникла бы потому, что IsQuickEnought знает точный тип своего аргумента (Runner) и использует специфические для этого типа функции и поля. А метод filterSportsmen (если бы приведенный в вопросе код был допустимым) передает в Predicate.test объекты про которые известно лишь то, что они наследуют от T( в этом случае Athlet) а значит туда можно передать объект любого типа унаследованный от Athlet, не обязательно Runner. Соответсвенно компилятор это запрещает.
Аналогично с producer-ом. Он может сгенерировать объект родительского класса, скажем Object, т.к. тип Supplier<? super T> позволяет передать туда Supplier<Object>. А понятно, что Object вставлять в sportsment нельзя. Если представить, что можно, то тогда возникает проблема в коде, который попробует вызвать getName у такого объекта.