If I am not wrong, you wish to have an explanation on how is the below valid code:
List<Number> nums = new ArrayList<Number>();
List<? super Number> sink = nums;
Invariance is property of the class on how its type parameter affects its subtyping.
Generics are invariant, but wild cards exist to help us with with sub-typing. They are not very useful as they dont representing anytype but represent a good hack. Below is valid
List<Animal> <: List<?>
List<?> <: List
Better example:
List<? super Animal> d1= new ArrayList<Animal>();
d1.add(new Animal());
d1.add(new Dog());
The above works, because d1
is of type List<? super Animal>
. You can imagine add
function to behave like:
boolean add(? super Animal e);
So to add
method you can pass any variable that is subtype of ? super Animal
. And
Animal <: ? super Animal
Dog <: ? super Animal
So adding a Dog or Animal works. So d1
acts as a list that can take any parameter of type Animal
or subtype if it.
Similarly, you can also have below. But technically you cant add anything to this list. If there existed sometype in java that is subtype of every type, then you could properly add element of that type to it. Nothing Else.
ArrayList<? extends Animal> d1 = new ArrayList<Animal>();
Look at this answer for more info.