(注意:这与this question about vs. 不相同!)
我对Java的泛型感到困惑。
ArrayList<? extends Object> x = new ArrayList<String>();
ArrayList<Object> y = new ArrayList<String>();
在此示例中,第一行编译而第二行不编译。为什么会这样?
我认为List<A> x = new ArrayList<B>()
形状的赋值只要B extends A
才有效,即,右侧的特异性高于左侧的特异性,但显然我弄错了。 / p>
有人可以详细说明这些陈述的异同吗?
答案 0 :(得分:2)
在Java中,类型参数必须完全匹配。
请考虑以下代码段:
ArrayList<Object> list = new ArrayList<String>();
list.add(new SomeClass());
我们现在已经成功地向ArrayList<String>
添加了非字符串。第二条语句没有错:我们正在向ArrayList<Object>
添加一些对象。因此,第一个语句肯定有问题!因此,Java不允许您这样做。
如果您将代码段更改为:
ArrayList<? extends Object> list = new ArrayList<String>();
list.add(new SomeClass());
您会发现错误现在在第二行。您不能将对象添加到列表中。
两者之间的区别如下:
ArrayList<Object>
的意思是“包含Object
s的ArrayList”。这样的列表当然也可以包含Object
子类的实例。由于每个对象都是Object
的实例,因此该列表可以包含任何对象(在这种情况下)。
ArrayList<? extends Object>
的意思是“一个包含Object
未知子类实例的ArrayList”。这样的ArrayList不能包含每个对象。只能将此未知子类的实例添加到其中。由于特定的子类未知,因此无法在此处添加元素。 (但在其他地方,同一列表可能被称为ArrayList<String>
,当然可以向其中添加元素。)
答案 1 :(得分:1)
以下两种情况都不起作用,因为初始化的对象类型与预期的不同:
ArrayList<Object> y = new ArrayList<String>();
ArrayList<String> y2 = new ArrayList<Object>();
如果您使用?
通配符,则可以得到更多的选择,使其具有不同的属性,但与继承类型相关(String
隐式扩展了Object
):
ArrayList<? extends Object> x = new ArrayList<String>();
还考虑使用diamond operator
ArrayList<Object> y = new ArrayList<>();
您可以用一组空的类型参数(<>)替换调用通用类的构造函数所需的类型参数,只要编译器可以从上下文中推断类型参数即可。这对尖括号被非正式地称为菱形。
答案 2 :(得分:0)
String abc = "abc";
Object x = abc;
有效但是
List<Object> list = new ArrayList<String>();
并非因为通用表达式如果不包含通配符,则必须完全匹配。
请注意,在声明方面最好使用List
(而不是ArrayList),因为它允许您在以后的阶段更改实现,例如到LinkedList
。