Java通配符令人困惑的例子

时间:2014-07-08 20:07:51

标签: java generics wildcard

我阅读了以下两个java泛型通配符链接

Difference between generic type and wildcard type

Are wildcard generics really needed?

我仍然不明白这个编译的通配符,

public void foo(List<List<?>> t) {
    t.add(new ArrayList<String>());
    t.add(new ArrayList<Integer>());
}

但这不,

public static void funct2(final List<?> list, final Object something) {
        list.add(something); // does not compile
    }

我们不是在第二个代码块中做第一个相同的事情吗?

4 个答案:

答案 0 :(得分:2)

第一个示例方法有一个List<List<?>>参数。泛型类型参数为List<?>,表示&#34;任何类型的任何List&#34;,因此它接受ArrayList<String>ArrayList<Integer>。在类型擦除之后,JVM无论如何都会看到ListArrayList。请注意,现在只有List<?>可以出现t,无论发生什么。但ArrayList<String>List<?>ArrayList<Integer>是{ {1}}也。

第二个示例方法有一个List<?>参数。泛型类型参数是一个简单的通配符 - 一种特定但未知的类型。它可以是List<?>List<String>List<Object>;编译器不知道。除了List<Foo>之外,编译器必须禁止调用add方法,因为它不能保证类型安全。要添加到列表中的null对象可以是something,而Integer可以是list所知道的所有对象。

不同之处在于,在第一个示例中,添加的两个对象都是List<Foo> s,而在第二个示例中,一个简单的List被传递给包含一个Object的{​​{1}}未知类型。第一个是允许的,第二个是不允许的,正如我上面所解释的那样。

List的语义是&#34;任何类型的列表列表&#34;,但List<List<?>>的语义是&#34;特定但未知类型的列表&#34;

答案 1 :(得分:1)

这很奇怪,有点难。我会尝试尽力解释。在第一个示例中,您添加的列表与通配符的模式匹配。换句话说,List<List<?>>是一个列表,可以包含任何类型的参数化列表,以及您添加的内容。

解释不同:列表是参数化的,但它仍然是一个列表,你添加List,这样很酷。

在第二个示例中,List<?>是一个可以包含任何类型对象的列表,但我们不知道该对象的类型是什么。由于我们不知道,您无法安全地添加任何类型。如果列表是List<Integer>somethingString,那么就不匹配,所以不要去。

这很奇怪,因为它看起来并不一致。有时您可以添加内容,有时您无法添加内容。但你必须推断出所要求的是什么,以及为什么。在紧要关头,编译器会告诉你。尝试从编译器所说的内容开始向后工作,它会帮助你理解你做错了什么(或者说是对的)。

答案 2 :(得分:1)

构造

List<?>

List<List<?>>

虽然表面非常相似,但实际上基本上有不同的语义。

第一个构造的含义,带有非嵌套通配符,是“List参数化的一些明确的,但在此上下文中是未知类型”。

带嵌套通配符的第二个构造的含义是“{{1>} 任何List s”。

这就是为什么你可以,例如,声明

List

但不是

class ListOfLists implements List<List<?>> {...}

同样,这是合法的:

class ListOfSomething implements List<?> {...}

但不是这样:

List<?> xs = new ArrayList<Integer>();

基于此,应该很清楚,您可以将任何类型的列表添加到List<List<?>> lists = new ArrayList<List<Integer>>(); ,但无法将List<List<?>>添加到Object因为它实际上可能是List<?>或任何其他类型,添加List<String>显然是非法的。

答案 3 :(得分:0)

在第二个示例中,您只能从列表中获取,您没有告诉编译器列表接收了哪种对象,因此something不会被List<?> list接受。

您需要更改以下内容:

public static <E> void funct2(final List<E> list, final E something) {
    list.add(something);
}