在Java中,List <object>与List <!-有何不同?扩展对象->?

时间:2018-11-12 14:01:31

标签: java generics inheritance types

(注意:这与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>

有人可以详细说明这些陈述的异同吗?

3 个答案:

答案 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