user_of_toster
@user_of_toster

Почему контравариантность связывают с Consumer'ом?

Полностью понятна ковариантность продюсера Array<? extends Shape> - если массив отдает фигуры, то он может отдать и более специализированный квадрат. (Потому что квадрат = фигура)

Но интуитивно не понятна контравариантность Consumer'a Array<? super Square>. То, что в массив можно добавить квадрат, не значит что туда можно добавить более абстрактную фигуру, и тем более Object.

Что не до конца понимаю?
  • Вопрос задан
  • 86 просмотров
Решения вопроса 1
@Mercury13
Программист на «си с крестами» и не только
Немного не так.
1. Пусть Producer выдаёт массив фигур. Тогда потомок может наладить правило: он отдаёт массив более специализированных фигур, например кругов. И пользователь, который видит контракт Producer’а, но не знает конкретной реализации, окажется в пределах контракта.
2. Consumer может наладить правило: он принимает массив не только квадратов, но и фигур, и вообще игровых объектов, и вообще всяких объектов. Например, Consumer-циркулярка будет вырезать прямоугольники из фанеры, Consumer-графопостроитель — любые фигуры из тонких линий, Consumer-менеджер игрового архива — фигуры, текстуры, звуки и вообще всё, что есть в игре. Точно так же, пользователь, который видит контракт Consumer’а, но не знает конкретной реализации, будет в рамках контракта любой реализации.

В общем, читайте «принцип подстановки Лисков». Потомок может ужесточать требования к себе и расслаблять требования к другим. Но не наоборот — иначе подставим тип-предок и выйдем из контракта. Отсюда название.

Но: в Java с Producer’ами ковариантность работает автоматически.
public interface Producer {
    ArrayList<? extends Object> produce();    
}

public class MyProducer implements Producer {
    @Override
    public ArrayList<Square> produce() {
        return new ArrayList<>();
    }   
}


А с consumer’ами такая автоматическая контравариантность не катит, приходится вручную проверять то, что подал пользователь. В этом никакого проигрыша в производительности нет, шаблонные функции Java, основанные на стирании типа, постоянно проверяют объекты на принадлежность к классу. Просто неудобство.
public interface Consumer {
    void consume(ArrayList<? super Square> x);
}

public class MyConsumer implements Consumer {
    @Override
    public void consume(ArrayList<? super Square> x) {
        var a = (Square)x.get(0);
    }
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы