We need to examinate two things:
- What does the wildcard mean in the signature
void doStuff(Foo<? super Bar> foo)
- What does the wildcard mean inside the method body
Java has only one very simple rule to decide the subtype relation between Foo<A>
and Foo<B>
: none is a subtype of the other. We say that generic types are invariant, and even if there's a rationale because the Java designers made it this way, from your point of view it's an arbitrary decision.
It's the angle brackets that confuse us, poor developers: we have no problem in accepting that FooBar
and FooQoo
are not related in any way; but for some reason we need to believe that Foo<Qoo>
and Foo<Bar>
are. No, that's not the case.
No matter how A and B relate to each other, X<A>
and X<B>
are not
related.
No matter how A and B relate to each other, X<A>
and X<B>
are not related.
No matter how A and B relate to each other, X<A>
and X<B>
are not related.
Once you are convinced of the above, please observe this snippet:
List<Double> doubles = ...;
List<Integer> integers = ...;
Number firstDouble = doubles.get(0);
Number firstInteger = integers.get(0);
Calling get(0)
on both lists gives us a Number-compatible object. We may want to put the call to get()
in a method like getFirstOfList(list)
but we just learned that such a method can't exist, because it would accept two totally unrelated types.
This is where wildcards come into play! We observe that calling get()
on a List<Number>
, a List<Integer>
, a List<Double>
(and so on) return a Number-compatible object (ie Number
or a subtype of it), so there must be a way to express this at the language level. The Java designers gave us the wildcard, which works like this: when you declare
public void doStuff(List<? extends Number> arg);
it has the same effect as declaring the following infinite list:
public void doStuff(List<Number> arg);
public void doStuff(List<Integer> arg);
public void doStuff(List<Double> arg);
public void doStuff(List<Float> arg);
public void doStuff(List<BigDecimal> arg);
...
Without the device of the wildcard you'd have to write one method for each supported list type (which btw is illegal in Java, but that's another story).
The wildcard I used in my example has an upper bound, identified by the extends
keyword. The snippet you pasted, instead, adopts a lower bounded wildcard in the method signature (super
). Actually it may contain some errors because for example:
- you can't pass a
List<Integer>
to doStuff()
- you can only get an
Object
from list.get(index)
so I will just tell you that the signature
void doStuff(List<? super Number> arg);
stands for the finite list:
void doStuff(List<Number> arg);
void doStuff(List<Object> arg);
and you can put any Number
you like in a List<? super Number>
but you'll only get()
Object
's from it.