问题
我有一个List<SubClass>
,我想把它视为aList<BaseClass>
。似乎它应该不是一个问题,因为将aSubClass
转换为aBaseClass
是一个快照,但我的编译器抱怨演员表是不可能的。
那么,获取对aList<BaseClass>
相同对象的引用的最佳方法是什么?
现在我只是制作一个新列表并复制旧列表:
List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)
但据我了解,必须创建一个全新的列表。如果可能的话,我想参考原始列表!
#1 热门回答(134 赞)
此类赋值的语法使用通配符:
List<SubClass> subs = ...;
List<? extends BaseClass> bases = subs;
重要的是要意识到aList<SubClass>
is不是可与aList<BaseClass>
互换。保留对List<SubClass>
的引用的代码将期望列表中的每个项目为aSubClass
。如果代码的另一部分称为aList<BaseClass>
,则编译器在插入aBaseClass
orAnotherSubClass
时不会抱怨。但是这将导致第一段代码aClassCastException
,它假定列表中的所有内容都是aSubClass
。
通用集合的行为与Java中的数组不同。数组是协变的;也就是说,允许这样做:
SubClass[] subs = ...;
BaseClass[] bases = subs;
这是允许的,因为数组"知道"其元素的类型。如果有人试图在数组中存储不是SubClass
实例的东西(通过bases
参考),则会抛出运行时异常。
通用集合不"知道"它们的组件类型;这些信息在编译时被"擦除"。因此,当发生无效存储时,它们无法引发运行时异常。相反,当从集合中读取值时,将在代码中的某个远距离,难以关联的点处引发aClassCastException
。如果你注意到有关类型安全性的编译器警告,你将在运行时避免这些类型错误。
#2 热门回答(29 赞)
erickson已经解释了为什么你不能这样做,但这里有一些解决方案:
如果你只想从基本列表中取出元素,原则上你的接收方法应声明为aList<? extends BaseClass>
。
但是如果它不是并且你无法更改它,则可以使用Collections.unmodifiableList(...)
包装列表,该列表允许返回参数参数的超类型的List。 (它通过在插入尝试时抛出UnsupportedOperationException来避免类型安全问题。)
#3 热门回答(10 赞)
正如@erickson解释的那样,如果你真的想要一个对原始列表的引用,那么如果你想在原始声明下再次使用它,请确保没有代码向该列表插入任何内容。获得它的最简单方法是将它转换为普通的旧非泛型列表:
List<BaseClass> baseList = (List)new ArrayList<SubClass>();
如果你不知道列表中发生了什么,我建议你更改列表中的任何代码以接受你拥有的列表。