Java не допускает Collection<Car>
в качестве подтипа Collection<Vehicle>
. Это потому, что Collection<Car>
нельзя использовать вместо каждого Collection<Vehicle>
, поскольку Collection<Vehicle>
может содержать элементы, которые являются другими подтипами транспортного средства, такими как мотоцикл, поэтому это нарушает принцип подстановки Лисков?
Подтипы коллекций - принцип подстановки Лисков
Ответы (4)
Как правило, Collection
s нарушают принцип подстановки Лискова из-за наличия «необязательных операций», т.е. методы мутации могут быть неприменимы для конкретной реализации.
Однако, что касается безопасности типов, это работает следующим образом:
Предполагая, что 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
типов не является подтипом другого.
Не совсем. Наоборот:
То, что Collection<Car>
содержит только Car
, это нормально, хотя Collection<Vehicle>
может содержать другие типы транспортных средств. Вы по-прежнему можете дать Collection<Car>
любому, кто может обрабатывать элементы из Collection<Vehicle>
.
Но Collection<Car>
нельзя использовать вместо Collection<Vehicle>
, потому что вы можете вставить Bicycle
в Collection<Vehicle>
(но не в Collection<Car>
). Таким образом, вы не можете отдать Collection<Car>
тому, кто хочет собирать автомобили.
Collection<T>
— это конструктор типа, поэтому Collection<Car>
— это тип, а Collection<Vehicle>
— другой тип.
Теперь вопрос, можно ли использовать Collection<Car>
там, где требуется Collection<Vehicle>
, является вопросом дисперсии (в данном случае ковариация).
Принцип подстановки Лисков превосходит соответствие типов в том смысле, что он не только требует предоставления большего и меньшего количества, но также и сохранения контракта супертипа, см. Википедию.
Обратите внимание, что Collection<Car>
инвариантен по отношению к типу компонента Car
. Поэтому, если вы хотите использовать набор транспортных средств, которые могут быть автомобилями, велосипедами, поездами и т. д., вам следует взять ковариантный набор Collection<? extends Vehicle>
.
Конечно, если у вас есть CarFactory
с методом product, который должен где-то парковать произведенные автомобили, контравариантный метод produce(Collection<? super Car>)
будет наиболее полезной реализацией.