Что было бы если компилятор не следовал правилам 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 шт):

Автор решения: Roman Konoval

Если бы компилятор это позволил, то возникли бы проблемы. Вот для примера с 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 у такого объекта.

→ Ссылка