Подтипы коллекций - принцип подстановки Лисков

Java не допускает Collection<Car> в качестве подтипа Collection<Vehicle>. Это потому, что Collection<Car> нельзя использовать вместо каждого Collection<Vehicle>, поскольку Collection<Vehicle> может содержать элементы, которые являются другими подтипами транспортного средства, такими как мотоцикл, поэтому это нарушает принцип подстановки Лисков?


person hazza147    schedule 22.04.2015    source источник
comment
Это больше связано с проблемой инвариантности при использовании дженериков и связанной с ней ковариационной реакцией.   -  person alainlompo    schedule 22.04.2015


Ответы (4)


Как правило, Collections нарушают принцип подстановки Лискова из-за наличия «необязательных операций», т.е. методы мутации могут быть неприменимы для конкретной реализации.

Однако, что касается безопасности типов, это работает следующим образом:

Предполагая, что Car является подтипом Vehicle, Collection<Car> является типом, который позволяет выполнять такие операции, как

Collection<Car> c=…;
Car car=c.iterator().next();

чего Collection<Vehicle> нет. С другой стороны, Collection<Vehicle> — это тип, который позволяет выполнять такие операции, как

Collection<Vehicle> c=…;
Vehicle v=…;
c.add(v);

чего Collection<Car> нет. Следовательно, ни один из этих Collection типов не является подтипом другого.

person Holger    schedule 22.04.2015
comment
См. stackoverflow.com/questions/22050848/ для обсуждения того, нарушают ли методы мутации коллекций LSP. - person jaco0646; 03.08.2016

Не совсем. Наоборот:

То, что Collection<Car> содержит только Car, это нормально, хотя Collection<Vehicle> может содержать другие типы транспортных средств. Вы по-прежнему можете дать Collection<Car> любому, кто может обрабатывать элементы из Collection<Vehicle>.

Но Collection<Car> нельзя использовать вместо Collection<Vehicle>, потому что вы можете вставить Bicycle в Collection<Vehicle> (но не в Collection<Car>). Таким образом, вы не можете отдать Collection<Car> тому, кто хочет собирать автомобили.

person Thilo    schedule 22.04.2015

Collection<T> — это конструктор типа, поэтому Collection<Car> — это тип, а Collection<Vehicle> — другой тип.

Теперь вопрос, можно ли использовать Collection<Car> там, где требуется Collection<Vehicle>, является вопросом дисперсии (в данном случае ковариация).

Принцип подстановки Лисков превосходит соответствие типов в том смысле, что он не только требует предоставления большего и меньшего количества, но также и сохранения контракта супертипа, см. Википедию.

person Stefan K.    schedule 22.04.2015

Обратите внимание, что Collection<Car> инвариантен по отношению к типу компонента Car. Поэтому, если вы хотите использовать набор транспортных средств, которые могут быть автомобилями, велосипедами, поездами и т. д., вам следует взять ковариантный набор Collection<? extends Vehicle>.

Конечно, если у вас есть CarFactory с методом product, который должен где-то парковать произведенные автомобили, контравариантный метод produce(Collection<? super Car>) будет наиболее полезной реализацией.

person llogiq    schedule 22.04.2015