我正在努力寻找合适的措词来回答我的问题(这可能就是为什么我无法使用Google),但归结为:为什么下面的行无效?
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。
ArrayList<MIPSInst> cannot be converted to List<AbstractInst<? extends IInstType>>
MIPSInst extends AbstractInst<MIPSInstType>
MIPSInstType implements IInstType
我已经阅读了有关泛型的Oracle文档,但显然这里缺少一些关键之处。朝正确方向轻推将不胜感激!
今天早些时候,我认为您想List<? extends AbstractInst<? extendsIInstType>>成为的数据类型insts。当然,这是将与您创建的对象匹配的数据类型,但是我怀疑在这种情况下它是否真的是您想要的。请按要求进行解释。
List<? extends AbstractInst<? extendsIInstType>>
insts
假设您有一个扩展了另一个类的类,例如PrintWriterextends Writer。就是说,每个PrintWriter也都是a Writer,但是还有一些额外的方法(例如println),您可以使用type变量来调用PrintWriter,但不能使用type变量来调用Writer。(与标准相比,我更喜欢真实的示例Dog extends Animal)。
PrintWriter
Writer
println
Dog extends Animal
重要的是要了解它ArrayList<PrintWriter>不是的子类型ArrayList<Writer>,尽管从直观上看可能不是。原因是这样的。假设我有一个变量myList,并且写了myList.add(new StringWriter()); 什么类型的myList?显然不能是类型ArrayList<PrintWriter>,因为a StringWriter不是a PrintWriter。但是它 可能 是类型ArrayList<Writer>,因为a StringWriter肯定是a Writer,因此必须能够将其添加到中ArrayList<Writer>。
ArrayList<PrintWriter>
ArrayList<Writer>
myList
myList.add(new StringWriter());
StringWriter
因此,任何ArrayList<Writer>行都可以正常工作
但任何ArrayList<PrintWriter>会 不会 。因此,从某种意义上来说,a ArrayList<PrintWriter>不可能是的特殊类型。ArrayList<Writer>``PrintWriter``Writer
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<? extendsWriter>必须实际上是一个ArrayList<Writer>,一个ArrayList<PrintWriter>或一个ArrayList<StringWriter>(或可能是ArrayList某种其他类型的Writer)。
ArrayList<StringWriter>
ArrayList<? extends Writer>
new ArrayList<? extends Writer>()
ArrayList<? extendsWriter>
ArrayList
这对我们来说应该不会太打扰。毕竟List<Writer>也是抽象类型。您无法编写文字new List<Writer>(),因为a List<Writer>实际上必须是ArrayList<Writer>或LinkedList<Writer>或其他类型的列表Writer。
List<Writer>
new List<Writer>()
LinkedList<Writer>
另外,类型变量ArrayList<? extends Writer>并不总是拥有最有用的变量,因为您不能使用它来向列表中添加内容。如果myList是类型的ArrayList<? extends Writer>,那么你就不能写myList.add(myWriter);不论类型是什么myWriter是,因为编译器无法检查的方式myWriter是 正确 的排序Writer的名单。
myList.add(myWriter);
myWriter
您可以使用类型变量ArrayList<? extends Writer>来将事情从列表中删除。如果myList是类型ArrayList<? extends Writer>,则 可以 写
Writer myWriter = myList.get(0);
因为无论myList指的是ArrayList<Writer>an ArrayList<PrintWriter>还是an ArrayList<StringWriter>,您都知道其中的含义是Writer。
所以回到实际问题,你在哪里
最初看来是合理的,因为正如您所解释的MIPSInst,的确是的子类型AbstractInst<? extendsIInstType>。或者换一种说法,每个MIPSInst都是一个AbstractInst<? extends IInstType>。
MIPSInst
AbstractInst<? extendsIInstType>
AbstractInst<? extends IInstType>
显然,您可能已经写过
List<MIPSInst> insts = new ArrayList<MIPSInst>();
并能够将内容添加到列表中,并从列表中删除内容。但是您想使用某种类型表达式来表明任何类型AbstractInst<? extendsIInstType>都可以,并且您只会将该变量用于可以在任何类型List的AbstractInst<? extendsIInstType>对象上使用的东西。
List
正如我在示例中展示StringWriter / PrintWriter的那样,您正在寻找的类型是List<? extends AbstractInst<? extends IInstType>>。这封装了以下事实:该列表可以是List任何类型的AbstractInst<?extends IInstType>,包括MIPSInst。
StringWriter / PrintWriter
List<? extends AbstractInst<? extends IInstType>>
AbstractInst<?extends IInstType>
但是,除非您将此变量强制转换为其他变量,否则使用此类型将限制您使用列表的只读视图。您可以get从列表中进行操作,但是却add一无所获,因为编译器无法检查添加的类型是否正确AbstractInst<?extends IInstType>。
get
add
在这种情况下,您将创建一个空列表。因此,引用可以让您get填充但不能add填充的引用可能不是很有用。因此,与我之前的评论相反,最好只是将变量声明为aList<MIPSInst>,然后将所有add事情都get放在心上。
List<MIPSInst>