将通用List分配给具体的ArrayList会导致编译时错误

时间:2013-11-17 23:24:04

标签: java generics

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

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

我收到ArrayList<MIPSInst> cannot be converted to List<AbstractInst<? extends IInstType>>的编译时错误。课程MIPSInst extends AbstractInst<MIPSInstType>MIPSInstType implements IInstType

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

1 个答案:

答案 0 :(得分:3)

今天早些时候,我认为您希望List<? extends AbstractInst<? extends IInstType>>成为insts的数据类型。它肯定是一种与您正在创建的对象匹配的数据类型,但我怀疑它是否实际上是您想要的。按顺序解释。

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

重要的是要理解ArrayList<PrintWriter>不是ArrayList<Writer>的子类型,尽管看起来可能是直观的。原因是这个。假设我有一个变量myList,我写myList.add(new StringWriter()); myList可以是哪种类型?它显然不能是ArrayList<PrintWriter>类型,因为StringWriter不是PrintWriter。但可以属于ArrayList<Writer>类型,因为StringWriter肯定是Writer,因此必须能够添加到ArrayList<Writer>

因此,任何ArrayList<Writer>在行

中都可以正常工作
myList.add(new StringWriter());

但是任何ArrayList<PrintWriter> 都不是。因此,ArrayList<PrintWriter>不一定是特殊类型的ArrayList<Writer>,与PrintWriterWriter的特殊类型相同。

换句话说,应该可以写这个

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

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

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

由于第二行显然是正常的,因此它必须是生成编译错误的第一行。确实如此 - 您无法将ArrayList<PrintWriter>分配给ArrayList<Writer>类型的变量,因为ArrayList<PrintWriter>只是不是 ArrayList<Writer> 。或者正如Jon Skeet在https://stackoverflow.com/a/2745301/1081110,“Awooga awooga”中表达的那样。

对于所有这些,ArrayList<Writer>ArrayList<PrintWriter>ArrayList<StringWriter>似乎都有点相似。似乎应该有某种类型的变量可以引用这三种变量中的任何一种。确实存在 - 它是ArrayList<? extends Writer>。但这是抽象类型。你无法实例化它。您无法撰写new ArrayList<? extends Writer>() - 最终ArrayList<? extends Writer>必须实际上是ArrayList<Writer>ArrayList<PrintWriter>ArrayList<StringWriter>(或可能是{{1}其他类型的ArrayList)。

这对我们来说不应该太令人不安。毕竟,Writer也是一种抽象类型。您无法撰写List<Writer>,因为new List<Writer>()实际上必须是List<Writer>ArrayList<Writer>,或其他类型的LinkedList<Writer>列表

此外,Writer类型的变量并不总是最有用的变量,因为您无法使用它来向列表中添加内容。如果ArrayList<? extends Writer>的类型为myList,那么无论ArrayList<? extends Writer>的类型是什么,都无法编写myList.add(myWriter);,因为编译器无法检查myWriter myWriter 1}}是列表的{em> right 类Writer

使用ArrayList<? extends Writer>类型的变量可以做的是将事情从列表中删除。如果myList的类型为ArrayList<? extends Writer>,那么可以

Writer myWriter = myList.get(0); 

因为myList无论ArrayList<Writer>ArrayList<PrintWriter>,还是ArrayList<StringWriter>还是Writer,您都知道其中的内容是某种List<AbstractInst<? extends IInstType>> insts = new ArrayList<MIPSInst>();

所以回到实际问题,你有

MIPSInst

最初似乎是合理的,因为正如您所解释的那样AbstractInst<? extends IInstType>确实是MIPSInst的子类型。换句话说,每AbstractInst<? extends IInstType>List<MIPSInst> insts = new ArrayList<MIPSInst>();

显然,你可以写

AbstractInst<? extends IInstType>

并且能够将内容添加到列表中,并从列表中获取内容。但是你想使用某种类型的表达式,表明任何类型的List都可以,并且你只会将这个变量用于任何类型AbstractInst<? extends IInstType> {{}}的东西。 1}}对象。

正如我在StringWriter / PrintWriter示例中所演示的那样,您要查找的类型是List<? extends AbstractInst<? extends IInstType>>。这说明了这个列表可以是任何类型List AbstractInst<? extends IInstType>的事实,包括MIPSInst

但是,除非将此变量强制转换为其他变量,否则使用此类型会将您限制为列表的只读视图。您可以get列表中的内容,但您根本无法add,因为编译器无法检查您是否添加了正确类型的AbstractInst<? extends IInstType>。< / p>

在这个特定实例中,您正在创建一个空列表。因此,对它进行引用可能不是非常有用,它可以让你get填充,而不是add。因此,与我之前的评论相反,如果您只是将变量声明为List<MIPSInst>,然后将add件事物和get事物声明为您心中的内容,则可能是最佳选择。