Java泛型:嵌套通配符参数的赋值

12

以下是代码示例:

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
}

第1、3和4行无法编译:

(第1行)

The method add(capture#1-of ?) in the type List<capture#1-of ?> is not applicable for the arguments (Abc<Def<Ghi<String>>>)

(第三行)

The method add(Abc<Def<?>>) in the type List<Abc<Def<?>>> is not applicable for the arguments (Abc<Def<Ghi<String>>>)

(第4行)

The method add(Abc<Def<Ghi<?>>>) in the type List<Abc<Def<Ghi<?>>>> is not applicable for the arguments (Abc<Def<Ghi<String>>>)

然而,第2行和第5行是可以编译的。

有人能解释一下为什么第1、3、4行不是合法的赋值语句吗?如果通配符参数不能在这些行中以那种方式使用,那么为什么第2行的赋值是合法的呢?


非常好的问题!对于line 1,这是因为除了null之外,您无法向未知类型的列表中添加任何内容。 - rlegendi
related... - Eugene
1个回答

12

listOne.add(abcdef)(第1行)无效,因为List<?>表示某种未知特定类型的列表。例如,它可以是List<String>,因此我们不希望添加任何不是String的内容。编译器错误发生是因为Abc<Def<Ghi<String>>>不能分配给?

listTwo.add(abcdef)(第2行)是有效的,因为List<Abc<?>>表示任何类型Abc列表。没错 - 嵌套通配符与顶级通配符不同,因为它们代表任何类型而不是某些特定类型(换句话说,嵌套通配符不会捕获)。编译器允许这样做,因为Abc<Def<Ghi<String>>>可分配给Abc<?>。请参阅此帖子以进一步讨论嵌套通配符:Multiple wildcards on a generic methods makes Java compiler (and me!) very confused

listThree.add(abcdef)(第3行)是无效的,因为List<Abc<Def<?>>>代表任意类型的DefAbc列表。泛型不是协变的,所以Abc<Def<Ghi<String>>>不能赋值给Abc<Def<?>>,尽管Def<Ghi<String>>可以赋值给Def<?>。同样的道理,List<Integer>不能赋值给List<Number>。更多解释请参考这篇文章:Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?

listFour.add(abcdef)(第4行)是无效的,原因是Abc<Def<Ghi<String>>>不能赋值给Abc<Def<Ghi<?>>>。请注意保留HTML标签。

listFive.add(abcdef)(第5行)是有效的,因为泛型类型完全匹配 - Abc<Def<Ghi<String>>>显然可以赋值给Abc<Def<Ghi<String>>>。请注意保留HTML标签。


5
想跟进一下,提醒一下,将第3行和第4行的声明从List<Abc<Def<?>>> listThree改为List<Abc<? extends Def<?>>> listThree,并将第4行的声明从List<Abc<Def<Ghi<?>>>>改为List<Abc<? extends Def<? extends Ghi<?>>>>可以解决可分配性问题。感谢简明扼要的解释和有用的链接,现在更清晰了。 - sumitsu
@PaulBellora _没错 - 嵌套通配符与顶层通配符不同,因为它们代表任何类型而不是某个特定类型_。非常正确! - Eugene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接