将通用列表分配给具体的ArrayList会导致编译时错误 [英] Assigning a generic List to a concrete ArrayList is causing a compile-time error

查看:121
本文介绍了将通用列表分配给具体的ArrayList会导致编译时错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很努力地为我的问题寻找合适的措辞(这可能是我为什么无法Google的原因),但归结为:为什么这条线以下无效?

 列表< AbstractInst< ;?扩展IInstType>> insts = new ArrayList< MIPSInst>(); 

我得到的编译时错误 ArrayList< MIPSInst>无法转换为List< AbstractInst< ;?扩展IInstType>> 。类 MIPSInst扩展AbstractInst< MIPSInstType> MIPSInstType implements IInstType



我已阅读有关泛型的Oracle文档,但我显然在这里失去了一些关键。向正确的方向微调将不胜感激!

解决方案

今天早些时候,我认为你会想列表与LT ;?扩展AbstractInst< ;?将IInstType>> 扩展为 insts 的数据类型。它当然是一种数据类型,可以与您创建的对象相匹配,但我怀疑这种情况下它是否实际上是您想要的。



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



理解 ArrayList< PrintWriter> 不是 ArrayList< Writer> 的子类型,尽管直观上看起来可能如此。原因是这样的。假设我有一个变量 myList ,并且我写了 myList.add(new StringWriter()); code> myList 是?它显然不能是 ArrayList< PrintWriter> 类型,因为 StringWriter 不是 PrintWriter的。但它可以是 ArrayList< Writer> 类型,因为 StringWriter 绝对是 Writer ,因此必须能够被添加到 ArrayList< Writer> 中。因此,任何 ArrayList< Writer> 都可以在行中正常工作

  myList.add(new StringWriter()); 

但任何 ArrayList< PrintWriter> EM>不。因此, 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());

由于第二行显然是OK,它必须是产生编译错误的第一行。实际上它是 - 你不能为 ArrayList< Writer> 类型的变量指定 ArrayList< PrintWriter> 因为 ArrayList< PrintWriter> 只是不是一个 ArrayList< Writer> 。或者Jon Skeet在 https://stackoverflow.com/a/2745301/1081110 ,Awooga awooga中表达过。

对于所有这些, ArrayList< Writer> ArrayList< PrintWriter> ArrayList< StringWriter> 似乎都有些相似。似乎应该有某种类型的变量可以指向这三种中的任何一种。确实有 - 它是 ArrayList<扩展Writer> 。但这是一个抽象类型。你不能实例化它。你不能写新的ArrayList<扩展Writer>() - 最终是一个 ArrayList<扩展Writer> 实际上必须是 ArrayList< Writer> ArrayList< PrintWriter> ArrayList< StringWriter> (或可能是 ArrayList 其他类型的 Writer )。



这对我们来说不应该太令人不安。毕竟, List< Writer> 也是一个抽象类型。你不能写新的List (),因为 List< Writer> 实际上必须是 ArrayList< Writer> LinkedList< Writer> ,或一些其他类型的 Writer



另外, ArrayList <? extends Writer> 并不总是最有用的变量,因为您无法使用它将其添加到列表中。如果 myList 类型为 ArrayList <? extends Writer> ,那么不管写什么类型的 myWriter都不能写 myList.add(myWriter); 是,因为编译器无法检查 myWriter 类的 Writer 作为列表。

您可以对 ArrayList <?>类型的变量做些什么? extends Writer> 就是让事情不在列表中。如果 myList 类型为 ArrayList <?扩展Writer> ,然后您可以写入

  Writer myWriter = myList获得(0); 

因为无论 myList 是指 ArrayList< Writer> ,an ArrayList< PrintWriter> ArrayList< StringWriter> code>,你知道它里面有什么是 Writer



所以要返回实际的问题,你有哪里

 列表< AbstractInst< ;?扩展IInstType>> insts = new ArrayList< MIPSInst>(); 

最初似乎是合理的,因为正如你已经解释 MIPSInst 确实是 AbstractInst的子类型?扩展IInstType> 。换句话说,每一个 MIPSInst 都是 AbstractInst< ;?扩展IInstType>



很明显,你可以写成

 列表与LT; MIPSInst> insts = new ArrayList< MIPSInst>(); 

,并且能够将其添加到列表中,并将事情从列表中删除。但是你想使用某种类型的表达式来表示任何一种 AbstractInst <?扩展IInstType> 是可以的,而且你只打算使用这个变量来处理 List of AbstractInst< ;?扩展IInstType> 对象。



正如我已经用我的 StringWriter / PrintWriter ,您寻找的类型是 List< ;?扩展AbstractInst< ;?扩展IInstType>> 。这封装了这样一个事实:这个列表可以是 List 任何类型的 AbstractInst< ;?扩展IInstType> ,包括 MIPSInst 。然而,除非你将这个变量转换为别的东西,否则使用这种类型将限制你到列表的只读视图。你可以从列表中找到获得的东西,但是你根本不能添加任何东西,因为编译器没有办法检查您是否添加了正确的 AbstractInst< ;?扩展IInstType>



在这个特定的实例中,您正在创建一个空列表。所以引用它可能会让你获得的东西,但不是添加的东西可能不是很有用。因此,与我之前的评论相反,如果您只是将变量声明为 List< MIPSInst> ,那么最好是 add 事情和获得您心中的内容。


I'm struggling to find propper wording for my question (which may be why I was unable to Google it), but it boils down to: why is the line below invalid?

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

I'm getting a compile-time error that ArrayList<MIPSInst> cannot be converted to List<AbstractInst<? extends IInstType>>. The class MIPSInst extends AbstractInst<MIPSInstType> and MIPSInstType implements IInstType.

I've read through the Oracle documentation on generics, but I'm clearly missing something key here. A nudge in the right direction would be greatly appreciated!

解决方案

Earlier today, I thought that you'd want List<? extends AbstractInst<? extends IInstType>> to be the data type of insts. It's certainly a data type that will match the object that you're creating, but I doubt whether it's actually what you want in this case. An explanation is in order.

Suppose you have one class that extends another, like PrintWriter extends Writer. That is, every PrintWriter is also a Writer, but there are extra methods (like println) that you can call with a variable of type PrintWriter, but not with a variable of type Writer. (I prefer real examples to the standard Dog extends Animal).

It's important to understand that ArrayList<PrintWriter> is not a subtype of ArrayList<Writer>, although intuitively it seems like it might be. The reason is this. Suppose I have a variable myList, and I write myList.add(new StringWriter()); What type could myList be? It clearly can't be of type ArrayList<PrintWriter>, because a StringWriter is not a PrintWriter. But it could be of type ArrayList<Writer>, because a StringWriter is definitely a Writer and therefore must be able to be added to an ArrayList<Writer>.

Therefore, any ArrayList<Writer> would work OK in the line

myList.add(new StringWriter());

but any ArrayList<PrintWriter> would not. Therefore, an ArrayList<PrintWriter> can't possibly be a special type of ArrayList<Writer>, in the same sense that a PrintWriter is a special type of Writer.

To put it another way, it should be possible to write this

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

and yet the compiler should somehow prevent us from writing this

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

Since the second line is clearly OK, it must be the first line that generates the compile error. Indeed it is - you can't assign an ArrayList<PrintWriter> to a variable of type ArrayList<Writer>, because an ArrayList<PrintWriter> just isn't an ArrayList<Writer>. Or as Jon Skeet expressed it at https://stackoverflow.com/a/2745301/1081110, "Awooga awooga".

For all of that, ArrayList<Writer>, ArrayList<PrintWriter> and ArrayList<StringWriter> all seem a little alike somehow. It seems there ought to be some type of variable that could refer to any one of these three. And indeed there is - it's ArrayList<? extends Writer>. But this is an abstract type. You can't instantiate it. You can't write new ArrayList<? extends Writer>() - ultimately an ArrayList<? extends Writer> has to actually be either an ArrayList<Writer>, an ArrayList<PrintWriter> or an ArrayList<StringWriter> (or possibly an ArrayList of some other type of Writer).

This shouldn't be too disturbing to us. After all, List<Writer> is also an abstract type. You can't write new List<Writer>(), because a List<Writer> actually has to be either an ArrayList<Writer>, or a LinkedList<Writer>, or some other type of list of Writer.

Also, a variable of type ArrayList<? extends Writer> isn't always the most useful variable to have, because you can't use it to add things to the list. If myList is of type ArrayList<? extends Writer>, then you can't write myList.add(myWriter); no matter what the type of myWriter is, because the compiler has no way of checking that myWriter is the right sort of Writer for the list.

What you can do with a variable of type ArrayList<? extends Writer> is to get things out of the list. If myList is of type ArrayList<? extends Writer>, then you can write

Writer myWriter = myList.get(0); 

because no matter whether myList refers to an ArrayList<Writer>, an ArrayList<PrintWriter> or an ArrayList<StringWriter>, you know that what's in it is some kind of Writer.

So to return to the actual question, where you have

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

which initially seems reasonable, since, as you've explained MIPSInst is indeed a subtype of AbstractInst<? extends IInstType>. Or to put it another way, every MIPSInst is an AbstractInst<? extends IInstType>.

Obviously, you could have written

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

and been able to add things to the list, and get things out of the list. But you wanted to use some type expression that indicated that any kind of AbstractInst<? extends IInstType> is OK, and that you're only going to use this variable for stuff that would work on any kind of List of AbstractInst<? extends IInstType> objects.

As I have demonstrated with my StringWriter / PrintWriter example, the type that you were looking for is List<? extends AbstractInst<? extends IInstType>>. That encapsulates the fact that this list can be a List of any type of AbstractInst<? extends IInstType>, including MIPSInst.

However, unless you cast this variable to something else, using this type will restrict you to a read-only view of the list. You can get things from the list, but you can't add anything at all, because the compiler has no way of checking that you're adding the right kind of AbstractInst<? extends IInstType>.

In this particular instance, you're creating an empty list. So it's probably not very useful to have a reference to it that lets you get stuff, but not add stuff. So contrary to my earlier comment, it's probably best if you just declare your variable as a List<MIPSInst>, then add things and get things to your heart's content.

这篇关于将通用列表分配给具体的ArrayList会导致编译时错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆