Search code examples
javaclassderived-classderivedparameterized-types

Parameterized Type Mismatch in Java


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?


Solution

  • 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