This is a really good question and the simple answer was guessed already:
For the current version of the fill(List<? super T> list, T obj)
there is no
such input that would be rejected given the signature is changed to fill(List<T> list, T obj)
, so there is no benefit and the devs are likely followed the PECS principle
The above statement derives from the principle that: if there is a such type X
so that
X
is a supertype of T
then List<X>
is a supertype of List<? super T>
because of type contravariance.
Since we can always find such X
(at the worst case it's the Object
class) - the compiler can infer a suitable List<X>
argument type given either form of fill
.
So, knowing that fact we can interfere with the compiler and infer the type ourselves using "type witness" so the code breaks:
List<Object> target = new ArrayList<>();
//Compiles OK as we can represent List<Object> as List<? super Integer> and it fits
Collections.<Integer>fill(target, 1);
//Compilation error as List<Object> is invariant to List<Integer> and not a valid substitute
Collections.<Integer>fillNew(target, 1);
This is all of course purely theoretical and nobody in their right mind would use the type argument there.
HOWEVER
While answering the question "What is the benefit of using wildcards here?" we yet considered only one side of the equation - us, consumers of the method and our experience but not library developers.
Hence this question is somewhat similar to why Collections.enumeration(final Collection<T> c)
is declared the way it is and not enumeration(Collection<T> c)
as final
seems superfluous for the end-user.
We can speculate here about the real intention, but I can give a few subjective reasons:
- First: using
List<? super T>
(as well as final
for enumeration
) immediately disambiguates the code that tiny bit more and for the <? super T>
specifically - it useful to show that only partial knowledge about the
type parameter is required and the list
cannot be used to produce values of T, but only to consume them.
Quote:
Wildcards are useful in situations where only partial knowledge about the type parameter is required.
JLS 4.5.1. Type Arguments of Parameterized Types
- Second: it gives some freedom to the library owners to improve/update the method without breaking backward compatibility while conforming to the existing constraints.
Now let's try make up some hypothetical "improvements" to see what I mean (I'll call the form of fill
that uses List<T>
as fillNew
):
#1 The decision is to make method to return the obj
value (used to fill up the list) back:
public static <T> void fill(List<? super T> list, T obj)
//becomes ↓↓↓
public static <T> T fill(List<? super T> list, T obj)
The updated method would work just fine for fill
signature, but for fillNew
- the inferred return type now isn't that obvious:
List<Number> target = new ArrayList<>();
Long val = fill(target, 1L); //<<Here Long is the most specific type that fits both arguments
//Compilation error
Long val = fillNew(target, 1L); //<<Here Number is, so it cannot be assigned back
//More exotic case:
Integer val = fill(asList(true), 0); //val is Integer as expected
Comparable<?> val = fillNew(asList(true), 0); //val is now Comparable<?> as the most specific type
#2 The decision to add an overloaded version of fill
that is 10x more performant in cases when T
is Comparable<T>
:
/* Extremely performant 10x version */
public static <T extends Comparable<T>> void fill(List<? super T> list, T value)
/* Normal version */
public static void fill(List<? super T> list, T value)
List<Number> target = new ArrayList<>();
fill(target, 1); //<<< Here the more performant version is used as T inferred to Integer and it implements Comparable<Integer>
fillNew(target, 1); //<< Still uses the slow version just because T is inferred to Number which is not Comparable
To sum up - the current signature of fill
is more flexible/descriptive in my opinion for all parties (developers and library designers)
T
is inferred to beInteger
(with the wildcard), whereas it's inferred to beNumber
in the second case (without the wildcard). Not positive when that would make a practical difference, but there is a difference. – Brokeragefill
. – Larentia