我正在努力为我的问题找到适当的措辞(这可能是我无法谷歌的原因),但它归结为:为什么下面的行无效?
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,但我在这里显然遗漏了一些关键。非常感谢正确方向的推动!
答案 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>
,与PrintWriter
是Writer
的特殊类型相同。
换句话说,应该可以写这个
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
事物声明为您心中的内容,则可能是最佳选择。