我有一个方法,其参数应该是"任何东西的列表(List of anything)"。该方法不会修改列表的内容。将此方法定义为以下哪个更加正确?
void foo(List<?> list) {
}
或者void foo(List<Object> list) {
}
那具体有什么区别呢?
简短回答:使用List<?>
可以接受类似于List<String>
的内容,而使用List<Object>
则不能。
[...] 这里是使用泛型和新的for循环语法进行编写的天真尝试:
void printCollection(Collection<Object> c) {
for (Object e : c) {
System.out.println(e);
}
}
问题在于这个新版本比旧版本要不实用得多。旧代码可以使用任何类型的集合作为参数进行调用,但新代码只接受 Collection<Object>
,正如我们刚刚演示的那样,并非所有类型的集合的超类型!Collection<?>
(读作“未知类型的集合”),也就是说,元素类型可以匹配任何东西的集合。 这被称为通配符类型,理由显而易见。我们可以这样写: void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
现在,我们可以使用任何类型的集合来调用它。
List<Object>
的参数化类型是 Object
,这意味着你可以将所有对象添加到列表中。List<?>
表示的是未知类型列表。只要你不打算向列表中添加任何内容,它基本上与 List<Object>
相同,但你需要关注以下情况:List<?> unknownList = new ArrayList<String>();
unknownList.add(new Object()); //Compilation error.
?
的意思是您只能添加 String
值或其子类型的值。
如果您只是要遍历列表并检索值,则 List<?>
和 List<Object>
本质上是相同的。它们两者相当不同
void foo(List<?> list)
意味着你期望得到某个列表,但你没有指定它是什么
void foo(List<Object> list)
表示您期望传递的列表是对象列表,因此如果尝试传递List,则不会被接受(与第一个声明相反)
常见错误是假设List<String>
是子类型,可以说是List<Object>
,即使String是Object的子类型也不是这种情况。
如果您不想修改列表,则使用 List<?>
更合适。
这样,您可以将例如 List<String>
和 List<BigInteger>
传递给您的方法。
你需要使用void foo(List<?> list)
,否则你将无法传递例如List<String>
。
如果你使用void foo(List<Object> list)
,那么你只能传递List<Object>
。
这是因为在Java中,泛型不是协变的: List<String>
不是List<Object>
的子类型。
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
引用自教程:
一般来说,如果Foo是Bar的子类型(子类或子接口),而G是某个泛型类型声明,则G<Foo>不是G<Bar>的子类型....
... Collection<Object>,正如我们刚刚证明的那样,它不是所有类型集合的超类型! 那么所有类型集合的超类型是什么?它被写成Collection<?>(发音为“未知集合”),即元素类型与任何类型匹配的集合。
然而,在严格假设方法foo将保持集合不变的情况下,实现上应该没有区别。