Skip to main content
deleted 10 characters in body
Source Link
outdev
  • 5.5k
  • 3
  • 25
  • 40

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()); 

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 it possible to introduce heap pollution at line 3.

 List<Dog> dogs = new ArrayList<>(); List<Animal> animals = dogs; // compile-time error, otherwise heap pollution animals.add(new Cat()); 

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 be possible to make heap pollution at line 3.

 List<Dog> dogs = new ArrayList<>(); List<Animal> animals = dogs; // compile-time error, otherwise heap pollution animals.add(new Cat()); 
Source Link
outdev
  • 5.5k
  • 3
  • 25
  • 40

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 it possible to introduce heap pollution at line 3.

 List<Dog> dogs = new ArrayList<>(); List<Animal> animals = dogs; // compile-time error, otherwise heap pollution animals.add(new Cat());