To understand the problem it's useful to make comparison to arrays.
List<Dog> is not subclass of List<Animal>.
But Dog[] is subclass of Animal[].
Arrays are reifiable and covariant.
Reifiable means their type information is fully available at runtime.
Therefore arrays provide runtime type safety but not compile-time type safety.
// All compiles but throws ArrayStoreException at runtime at last line Dog[] dogs = new Dog[10]; Animal[] animals = dogs; // compiles animals[0] = new Cat(); // throws ArrayStoreException at runtime It's vice versa for generics:
Generics are erased and invariant.
Therefore generics can't provide runtime type safety, but they provide compile-time type safety.
In the code below if generics were covariant it will make itbe possible to introducemake heap pollution at line 3.
List<Dog> dogs = new ArrayList<>(); List<Animal> animals = dogs; // compile-time error, otherwise heap pollution animals.add(new Cat());