For the following code sample:
public static class Abc<X> { }
public static class Def<Y> { }
public static class Ghi<Z> { }
public void doThis() {
List<?> listOne;
List<Abc<?>> listTwo;
List<Abc<Def<?>>> listThree;
List<Abc<Def<Ghi<?>>>> listFour;
List<Abc<Def<Ghi<String>>>> listFive;
Abc<Def<Ghi<String>>> abcdef;
abcdef = new Abc<Def<Ghi<String>>>();
listOne.add(abcdef); // line 1
listTwo.add(abcdef); // line 2
listThree.add(abcdef); // line 3
listFour.add(abcdef); // line 4
listFive.add(abcdef); // line 5
}
Lines 1, 3, and 4 do not compile:
(line 1)
The method add(capture#1-of ?) in the type List<capture#1-of ?> is not applicable for the arguments (Abc<Def<Ghi<String>>>)
(line 3)
The method add(Abc<Def<?>>) in the type List<Abc<Def<?>>> is not applicable for the arguments (Abc<Def<Ghi<String>>>)
(line 4)
The method add(Abc<Def<Ghi<?>>>) in the type List<Abc<Def<Ghi<?>>>> is not applicable for the arguments (Abc<Def<Ghi<String>>>)
Lines 2 and 5, however, compile.
Could anyone explain why lines 1, 3, and 4 are not legal assignments? And if wildcard parameters cannot be used in that way on those lines, then why is the assignment on line 2 legal?
listOne.add(abcdef)
(line 1) is invalid because List<?>
represents a list of some unknown specific type. For example, it could be a List<String>
, so we wouldn't want to add anything that isn't a String
. The compiler error happens because Abc<Def<Ghi<String>>>
is not assignable to ?
.
listTwo.add(abcdef)
(line 2) is valid because List<Abc<?>>
represents a list of Abc
s of any type. That's right - nested wildcards are different from top-level wildcards in that they represent any type rather than some specific type (in other words, nested wildcards don't capture). The compiler allows it because Abc<Def<Ghi<String>>>
is assignable to Abc<?>
. See this post for further discussion of nested wildcards: Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
listThree.add(abcdef)
(line 3) is invalid because List<Abc<Def<?>>>
represents a list of Abc
s of Def
s of any type. Generics are not covariant, so Abc<Def<Ghi<String>>>
is not assignable to Abc<Def<?>>
, even though Def<Ghi<String>>
is assignable to Def<?>
. A List<Integer>
isn't assignable to a List<Number>
for the same reason. See this post for further explanation: Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?
listFour.add(abcdef)
(line 4) is invalid for the same reason - Abc<Def<Ghi<String>>>
is not assignable to Abc<Def<Ghi<?>>>
.
listFive.add(abcdef)
(line 5) is valid because the generic types match exactly - Abc<Def<Ghi<String>>>
is obviously assignable to Abc<Def<Ghi<String>>>
.