You can think about this quite easily. A List<? extends SomeClass>
will always produce an implementation of SomeClass
. See the following example:
List<Integer> ints = new Arrays.asList(1,2,3,4);
List<? extends Number> nums = ints; // this does work
Integer i = nums.get(0); // this doesn't
Number num = nums.get(0); // this does again
nums.add(5); // this doesn't too
You see that nums
holds a reference to ints
. Because Integer
extends Number
. And if you read the nums
-list you'll always get a Number
out.
This holds true for every superclass-subclass relationship.
But: you can't write to the nums
list. That is, because you don't exactly know what list actually is referenced by nums
in the above example it was a list of Integer
s, you just know what to expect from the list when you read it.
The same is for List<? super SomeClass>
which is the invert of the above. It will always consume any implementations of SomeClass
. See again the example:
List<Object> objs = new ArrayList<>();
List<? super String> strings = objs; // this does work
strings.add("Some String"); // this works
String string = strings.get(0); // this doesn't
Object object = strings.get(0); // this does again
Now we only know that every element of type String
and more specific can be put into the Strings
list. As this is the invert of the above. You don't know what you will get, if you try to read the list. You can just read Object
s but you will never know their actual class.
This is generally called PECS, which stands for: Producer extends
Consumer super
. More about this topic can be read in this question