【问题标题】:Assigning a generic List to a concrete ArrayList is causing a compile-time error [duplicate]将通用列表分配给具体的 ArrayList 会导致编译时错误 [重复]
【发布时间】:2013-11-17 23:24:04
【问题描述】:

我正在努力为我的问题找到合适的措辞(这可能是我无法谷歌搜索的原因),但归结为:为什么下面的行无效?

List<AbstractInst<? extends IInstType>> insts = new ArrayList<MIPSInst>();

我收到ArrayList&lt;MIPSInst&gt; cannot be converted to List&lt;AbstractInst&lt;? extends IInstType&gt;&gt; 的编译时错误。 MIPSInst extends AbstractInst&lt;MIPSInstType&gt;MIPSInstType implements IInstType 类。

我已经阅读了Oracle documentation on generics,但我显然在这里遗漏了一些关键。非常感谢您朝正确的方向轻推!

【问题讨论】:

  • 您希望List&lt;? extends AbstractInst&lt;? extends IInstType&gt;&gt; 的数据类型为inst。如果我稍后有时间,我会发布一个带有详细解释的答案。
  • 噢!谢谢,@DavidWallace!
  • @DavidWallace:他为什么要在变量声明中使用额外的通配符?这不是只对参数签名有意义吗?
  • 这些通配符是为了使方法和类具有通用性,即在类型方面具有灵活性。我不明白你为什么要在变量声明中使用它。
  • @Thilo - 不知道你为什么删除你的答案。它对我来说看起来不错,并且提供了其他人没有提供的有价值的信息(比如插入到类型参数为 wild 的列表中的问题)。如果您取消删除它,我会支持它。

标签: java generics


【解决方案1】:

今天早些时候,我认为您希望 List&lt;? extends AbstractInst&lt;? extends IInstType&gt;&gt; 成为 insts 的数据类型。它肯定是一种与您正在创建的对象匹配的数据类型,但我怀疑在这种情况下它是否真的是您想要的。一个解释是为了。

假设您有一个类扩展了另一个类,例如PrintWriter 扩展Writer。也就是说,每个PrintWriter 也是一个Writer,但还有一些额外的方法(如println)可以用PrintWriter 类型的变量调用,但不能用Writer 类型的变量调用。 (比起标准的Dog extends Animal,我更喜欢真实的例子)。

了解ArrayList&lt;PrintWriter&gt; 不是ArrayList&lt;Writer&gt; 的子类型很重要,尽管直观上看起来可能是这样。原因是这样的。假设我有一个变量myList,我写myList.add(new StringWriter());myList 可以是什么类型?它显然不能是ArrayList&lt;PrintWriter&gt; 类型,因为StringWriter 不是PrintWriter。但它可以ArrayList&lt;Writer&gt; 类型,因为StringWriter 肯定是Writer,因此必须能够添加到ArrayList&lt;Writer&gt;

因此,任何ArrayList&lt;Writer&gt; 在行中都可以正常工作

myList.add(new StringWriter());

但任何ArrayList&lt;PrintWriter&gt;不会。因此,ArrayList&lt;PrintWriter&gt; 不可能是 ArrayList&lt;Writer&gt; 的特殊类型,就像 PrintWriterWriter 的特殊类型一样。

换一种说法,应该可以这样写

ArrayList<Writer> myList = new ArrayList<Writer>();
myList.add(new StringWriter());

然而编译器应该以某种方式阻止我们写这个

ArrayList<Writer> myList = new ArrayList<PrintWriter>();
myList.add(new StringWriter());

既然第二行明显OK,那肯定是第一行产生了编译错误。确实如此 - 您不能将 ArrayList&lt;PrintWriter&gt; 分配给 ArrayList&lt;Writer&gt; 类型的变量,因为 ArrayList&lt;PrintWriter&gt; 只是 不是 ArrayList&lt;Writer&gt;。或者正如 Jon Skeet 在 https://stackoverflow.com/a/2745301/1081110 上所说的那样,“Awooga awooga”。

尽管如此,ArrayList&lt;Writer&gt;ArrayList&lt;PrintWriter&gt;ArrayList&lt;StringWriter&gt; 在某种程度上看起来有点相似。似乎应该有某种类型的变量可以引用这三个中的任何一个。确实有 - 它是ArrayList&lt;? extends Writer&gt;。但这是一个抽象类型。你不能实例化它。你不能写new ArrayList&lt;? extends Writer&gt;()——最终ArrayList&lt;? extends Writer&gt;实际上必须是ArrayList&lt;Writer&gt;ArrayList&lt;PrintWriter&gt;ArrayList&lt;StringWriter&gt;(或者可能是ArrayList或其他类型的Writer)。

这对我们来说应该不会太令人不安。毕竟List&lt;Writer&gt;也是一个抽象类型。你不能写new List&lt;Writer&gt;(),因为List&lt;Writer&gt;实际上必须是ArrayList&lt;Writer&gt;,或LinkedList&lt;Writer&gt;,或其他类型的Writer列表。

此外,ArrayList&lt;? extends Writer&gt; 类型的变量并不总是最有用的变量,因为您不能使用它向列表中添加内容。如果myListArrayList&lt;? extends Writer&gt; 类型,那么无论myWriter 是什么类型都不能写myList.add(myWriter);,因为编译器无法检查myWriter 是不是正确的 /em> 类似于Writer 的列表。

你可以用ArrayList&lt;? extends Writer&gt; 类型的变量做的就是把事情从列表中取出。如果myListArrayList&lt;? extends Writer&gt; 类型,那么你可以

Writer myWriter = myList.get(0); 

因为无论myList 是指ArrayList&lt;Writer&gt;ArrayList&lt;PrintWriter&gt; 还是ArrayList&lt;StringWriter&gt;,你都知道其中的内容是某种Writer

所以回到实际的问题,你在哪里

List<AbstractInst<? extends IInstType>> insts = new ArrayList<MIPSInst>();

这最初似乎是合理的,因为正如您所解释的,MIPSInst 确实是AbstractInst&lt;? extends IInstType&gt; 的子类型。或者换句话说,每个MIPSInst 都是一个AbstractInst&lt;? extends IInstType&gt;

显然,你可以写

List<MIPSInst> insts = new ArrayList<MIPSInst>();

并且能够将事物添加到列表中,并将事物从列表中取出。但是您想使用某种类型表达式来表明任何类型的 AbstractInst&lt;? extends IInstType&gt; 都可以,并且您只会将此变量用于适用于任何类型的 ListAbstractInst&lt;? extends IInstType&gt; 对象的东西。

正如我在StringWriter / PrintWriter 示例中所展示的,您要查找的类型是List&lt;? extends AbstractInst&lt;? extends IInstType&gt;&gt;。这封装了这个列表可以是任何类型的AbstractInst&lt;? extends IInstType&gt;List,包括MIPSInst

但是,除非您将此变量转换为其他变量,否则使用此类型会将您限制为列表的只读视图。你可以get列表中的东西,但你不能add任何东西,因为编译器无法检查你是否添加了正确的AbstractInst&lt;? extends IInstType&gt;

在这个特定的实例中,您正在创建一个空列表。所以引用它可能不是很有用,它可以让你get 东西,但不是add 东西。因此,与我之前的评论相反,您最好将变量声明为 List&lt;MIPSInst&gt;,然后将 addget 声明为您心中的内容。

【讨论】:

    猜你喜欢
    • 2023-02-18
    • 2014-01-12
    • 1970-01-01
    • 2017-01-11
    • 1970-01-01
    • 1970-01-01
    • 2015-08-06
    • 1970-01-01
    • 2013-02-05
    相关资源
    最近更新 更多