为啥这一行不编译 List<Object> l = new ArrayList<String>()? [复制]

Posted

技术标签:

【中文标题】为啥这一行不编译 List<Object> l = new ArrayList<String>()? [复制]【英文标题】:Why does this line not compile List<Object> l = new ArrayList<String>()? [duplicate]为什么这一行不编译 List<Object> l = new ArrayList<String>()? [复制] 【发布时间】:2018-05-19 05:08:50 【问题描述】:

我希望能够列出这样的清单 -

List<Object> l = new ArrayList<String>();

ArrayList 的标准库实现不允许这样做。它给出了编译时错误。所以我正在编写自己的ArrayList 实现。

到目前为止,我已经尝试过这个 -

public class ArrayList<Y extends X> implements List<X> ...
public class ArrayList<X super Y> implements List<X> ...

不编译。

那么,我该如何编写一个 ArrayList 以便我可以像上面提到的那样使用它?

List<Object> l = new ArrayList<String>();

有人指出了这篇文章 Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?

但我还是不明白。给出的原因是我们想避免这样的情况 -

List<Object> l = new ArrayList<String>();
l.add(new Dog()); 

虽然这是有道理的,但我可以用 Java Arrays 做类似的事情,它可以编译但在 runtime抛出异常。

Object o[] = new String[10];
o[0] = new Dog();

那么允许后者代码编译但不允许前者编译的原因是什么?

编辑 - 类似的问题已经被问过,如上所述。 这个问题也有帮助Why is the ArrayStoreException a RuntimeException?

【问题讨论】:

为什么链接的问题没有回答您的问题?我的意思是它解释了为什么 Java 不支持这种语法。如果不是这样,您到底想达到什么目标?如果您编写 new ArrayList&lt;String&gt;(),该列表将接受 String 及其子类型。如果你改为写new ArrayList&lt;Object&gt;(),你可以添加所有你想要的。 你是说你宁愿在运行时出现异常而不是编译错误?这似乎没有多大意义。 不,我不是这么说的。我只是问这背后的原因是什么。 嗯,泛型的全部意义在于避免运行时异常,所以……你还需要什么其他理由? 我需要解释为什么数组允许同样的事情,在这种情况下为什么不编译时错误?基本上我要问的是为什么我们有这个双重政策,一个针对数组,另一个针对通用数组列表? 【参考方案1】:

正如您的链接所说,像这样定义您的List

List<? extends Object> l = new ArrayList<String>();

编辑

是的,原因

List<? extends Object> l = new ArrayList<String>(); l.add("asda");

不起作用是为了避免运行时异常,因为列表的类型可能是任何扩展 Object 的东西,所以编译器会这样对待它(可能是任何东西/未知)。但是,如果您保留对原始列表的引用,您仍然可以编辑列表。

List<String> strList = new ArrayList<>();
List<? extends Object> objList = strList;
strList.add("asdf");
// Will return Strings that only have the Object interface
Object o = objList.get(0);

List&lt;? extends Object&gt; 的目的只是为您的代码提供类型保证(在这种情况下,某些类型扩展 Object)并且由于泛型,编译器能够在编译时确保该条件,但它还需要以确保您不违反原始列表。以如下代码为例:

List<Integer> intList = new ArrayList<>();
List<? extends Object> objList = intList;
// compile error to protect the original list
objList.add("asdf");

所以因为objList? 类型,所以你不能向它添加String。尽管StringObject 的子类,但它可能与? 的类型不同(在这种情况下也不是)。

也不要忘记泛型类型在运行时因为类型擦除而消失了。泛型是编译时的保证。

另外,现在我看到了你真正的问题。

我需要解释为什么数组允许同样的事情,在这种情况下为什么不编译时错误?基本上我要问的是为什么我们有这个双重政策,一个针对数组,另一个针对通用数组列表?

查看this问题及其相关答案

【讨论】:

List&lt;? extends Object&gt; l = new ArrayList&lt;String&gt;(); l.add("asda"); 还是不行。【参考方案2】:

你可以写

List<Object> l = new ArrayList<Object>();

类型参数只在编译时存在,所以这没什么区别。

【讨论】:

以上是关于为啥这一行不编译 List<Object> l = new ArrayList<String>()? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能从 List<MyClass> 转换为 List<object>?

如何将 List<Object> 转换为 List<MyClass>

我不明白为啥会这样编译

<__main__.List object at 0x0293B950>,输出结果是这个为啥

为啥首选 Java 类的接口?

为啥以下程序在使用 g++ 编译时会慢 15%?