我阅读了以下两个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
}
我们不是在第二个代码块中做第一个相同的事情吗?
答案 0 :(得分:2)
第一个示例方法有一个List<List<?>>
参数。泛型类型参数为List<?>
,表示&#34;任何类型的任何List
&#34;,因此它接受ArrayList<String>
和ArrayList<Integer>
。在类型擦除之后,JVM无论如何都会看到List
和ArrayList
。请注意,现在只有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>
且something
是String
,那么就不匹配,所以不要去。
这很奇怪,因为它看起来并不一致。有时您可以添加内容,有时您无法添加内容。但你必须推断出所要求的是什么,以及为什么。在紧要关头,编译器会告诉你。尝试从编译器所说的内容开始向后工作,它会帮助你理解你做错了什么(或者说是对的)。
答案 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);
}