I have three types, A, B, and X
A<T extends Object> extends X
B extends Object
And there's this api call
doSomething(List<X>)
And I'm doing this call
doSomething(ImmutableList.<A<B>>of(new A<B>(), new A<B>()))
But I'm getting error:
List<X> cannot be applied to ImmutableList<A<B>>
Shouldn't A essentially be X? How to make this working?
Use the declaration with an upper bounded wildcard
doSomething(List<? extends X>)
List<S>
is not a subtype of List<T>
even if S
is a subtype of T
You can think about why this is in quite simple terms.
If Dog
and Cat
are subtypes of Animal
and your method is doSomething(List<Animal> pets)
, then inside the method you might add
a Cat
. But what if pets
was passed as a List<Dog>
? You might get an allergic reaction.
Enter the wildcard
You can therefore use wildcards to do this. The wildcard parameterized type List<? extends Animal>
represents: "(a type from) the set of all parameterized types - formed by invoking the generic type List<T>
- with a type argument that is any type (usually called an "an unknown type") - that is a subtype of Animal
(inclusive)".
By implication this is a potentially infinite set, as you could have an infinite number of subtypes of Animal
(and subtypes of List mumble mumble complicating issue mumble).
"in" parameters
Using List<? extends Animal>
says "I agree to treat everything in this list as, at best, an Animal
". In simple terms you can no longer add(e)
anything to that list because inside the method you don't know if it is a List<Dog>
, List<Cat>
or List<Animal>
at the calling site. You can however get(index)
any item from that list parameter and feed()
it (assuming feed()
is a method on Animal
). It is an "in" parameter: it provides data to the method.
tldr: An extends
bound implies an "in" variable (one that you extract data from, not one that you put data into)
"out" parameters
If you want to add(e)
a Dog
into the list you need to change the method declaration to an "out" variable by declaring doSomething(List<? super Dog>)
. Now you are saying "I can treat this list as holding any type that is a supertype of Dog
": at the calling site it may be a List<Dog>
, List<Animal>
or List<Object>
. You can now add(e)
either a Dog
, or a subtype of Dog
, like a Mongrel
: all will be compatible with any of the possible types of list that are passed. You can't add a Cat
as that would make no sense: the list at the calling site could still be a List<Dog>
, not a List<Animal>
.
tldr: It is an "out" parameter: it "stores" data from inside the method, (and can therefore provide it back to the calling site).
Refs: The Java Tutorials > Upper bounded wildcards