将List <SubClass>强制转换为List <BaseClass>的最有效方法

问题

我有一个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>,则编译器在插入aBaseClassorAnotherSubClass时不会抱怨。但是这将导致第一段代码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>();

如果你不知道列表中发生了什么,我建议你更改列表中的任何代码以接受你拥有的列表。