First of all, you should not confuse wildcard type parameters with mutability. Having a wildcard in a List
’s element type does not prevent modifications, it only imposes a few practical restrictions to what you can do with the list.
Having a list declared like List<? extends Number>
implies that the referenced list has an actual element type of Number
or a subclass of Number
, e.g. it could be a List<Integer>
or List<Double>
. So you can’t add an arbitrary Number
instance as you can’t know whether it is compatible to the actual element type.
But you can still add null
, as the null
reference is known to be compatible with all reference types. Further, you can always remove elements from a list, e.g. call remove
or clear
without problems. You can also call methods like Collections.swap(list, index1, index2)
, which is interesting as it wouldn’t be legal to call list.set(index1, list.get(index2))
due to formal rules regarding wildcard types, but passing the list to another method that might use a non-wildcard type variable to represent the list’s element type works. It’s obviously correct, as it only sets elements stemming from the same list, which must be compatible.
Likewise, if you have a Comparator<Number>
, you can call Collections.sort(list, comparator)
, as a comparator which can handle arbitrary numbers, will be able to handle whatever numbers are actually stored in the list.
To sum it up, having ? extends
in a collection’s element type does not prevent modifications.
As said, you can’t insert arbitrary new elements into a list whose actual element type might be an unknown subclass of the bound, like with List<? extends Number>
. But you are guaranteed to get a Number
instance when retrieving an element, as every instance of subtype of Number
is also an instance of Number
. When you declare a List<? super Number>
, its actual element type might be Number
or a super type of Number
, e.g. Object
or Serializable
. You can insert arbitrary Number
instances, as you know it will be compatible to whatever actual element type the list has, as it is a super type of number. When you retrieve an instance, you only know that it is an instance of Object
, as that’s the super type of all instances. To compare with the ? extends
case, having a ? super
declaration does not prevent reading, it only imposes some practical limitations. And likewise, you can still pass it to Collections.swap
, because, regardless of how little we known about the actual type, inserting what we just retrieved from the same list, works.
In your second question, you are confusing the sides. You are now not looking at the implementation of min
, but at the caller. The declaration of min(Comparator<? super T> c)
allows the caller to pass any comparator being parameterized with T
or a super type of T
. So when you have a Stream<String>
, it is valid to pass a Comparator<String>
to the min
method, which is exactly, what you are implementing via the (s1, s2) -> s1.length()—s2.length()
lambda expression (though, I’d prefer Comparator.comparingInt(String::length)
).
Within the implementation of min
, there is indeed no knowledge about what either, T
or the actual type argument of the Comparator
, is. But it’s sufficient to know that any stream element that is of type T
can be passed to the comparator’s compare
method, which might expect T
or a super type of T
.