2

Java does not permit Collection<Car> as a subtype of Collection<Vehicle>. Is this because Collection<Car> cannot be used in place of every Collection<Vehicle> as the Collection<Vehicle> could contain elements which are other subtypes of Vehicle such as Motorbike, therefore it violates Liskov substitution principle?

lbalazscs
  • 17,474
  • 7
  • 42
  • 50
hazza147
  • 21
  • 1
  • This is more related to the invariance challenge in using generics and the covariance response that goes with it – alainlompo Apr 22 '15 at 11:08

4 Answers4

1

Generally, Collections violate the Liskov substitution principle due to the existence of “optional operations”, i.e. the mutation methods may be unusable for a particular implementation.

Regarding the type safety, however, it works that way:

Assuming that Car is a subtype of Vehicle, a Collection<Car> is a type that allows an operation like

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

which Collection<Vehicle> doesn’t. On the other hand, Collection<Vehicle> is a type that allows an operation like

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

which Collection<Car> doesn’t. Therefore, neither of these Collection types is a subtype of the other.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 1
    See http://stackoverflow.com/questions/22050848/do-collections-unmodifiablexxx-methods-violate-lsp for a debate on whether collection mutation methods violate LSP. – jaco0646 Aug 03 '16 at 15:42
0

Not exactly. The other way around:

That a Collection<Car> only contains Cars is fine, even though a Collection<Vehicle> might contain other types of vehicles. You can still give a Collection<Car> to anyone who can handle elements from a Collection<Vehicle>.

But Collection<Car> cannot be used in place of a Collection<Vehicle>, because you could put a Bicycle into a Collection<Vehicle> (but not in a Collection<Car>). So you cannot give the Collection<Car> to someone who wants to collect vehicles.

Thilo
  • 257,207
  • 101
  • 511
  • 656
0

Collection<T> is a type constructor, so Collection<Car> is a type and Collection<Vehicle> is another type.

Now the question, if a Collection<Car> can be used where a Collection<Vehicle> is required, is a matter of variance (co-variance is this case).

Liskov substitution principle exceed type conformity in a way that it not only requires to provide more, require less, but also that the contract of the supertype is kept, see Wikipedia.

Stefan K.
  • 7,701
  • 6
  • 52
  • 64
0

Note that a Collection<Car> is invariant over the component type Car. So if you want to use a collection of vehicles which could be cars, bikes, trains, etc., you should take a covariant collection Collection<? extends Vehicle>.

Of course, if you have a CarFactory with a produce method that should park the produced cars somewhere, a contravariant produce(Collection<? super Car>) method would be the most useful implementation.

llogiq
  • 13,815
  • 8
  • 40
  • 72