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

我有一个List<SubClass> ,我想作为一个List<BaseClass> 。 看起来这不应该是一个问题,因为投下一个SubClass到一个BaseClass是一个快照,但我的编译器抱怨说,转换是不可能的。

那么,什么是最好的方式来获得一个List<BaseClass>相同的对象的引用?

现在我只是创build一个新的列表,并复制旧的列表:

 List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass) 

但据我所知,这必须创build一个全新的名单。 如果可能,我想参考原始列表!

这种分配的语法使用通配符:

 List<SubClass> subs = ...; List<? extends BaseClass> bases = subs; 

意识到List<SubClass> 不能List<BaseClass>互换是很重要的。 保留对List<SubClass>的引用的代码将期望列表中的每个项目都是一个SubClass 。 如果代码的另一部分作为List<BaseClass>引用列表,编译器将不会在插入BaseClassAnotherSubClass时发生抱怨。 但是,这将导致第一段代码的ClassCastException ,它假定列表中的所有内容都是一个SubClass

generics集合与Java中的数组行为不同。 数组是协变的; 也就是说,允许这样做:

 SubClass[] subs = ...; BaseClass[] bases = subs; 

这是允许的,因为数组“知道”其元素的types。 如果有人试图在数组中存储不是SubClass实例的东西(通过bases引用),则会抛出一个运行时exception。

通用集合 “知道”他们的组件types; 这个信息在编译时被“擦除”。 因此,当发生无效存储时,它们不能引发运行时exception。 相反,当从集合中读取一个值时,会在代码中的一些远距离,难以联系的地方引发ClassCastException 。 如果你注意编译有关types安全的警告,你将会在运行时避免这些types的错误。

埃里克森已经解释了为什么你不能这样做,但在这里有一些解决scheme:

如果你只想从你的基础列表中取出元素,原则上你的接收方法应该被声明为一个List<? extends BaseClass> List<? extends BaseClass>

但是,如果它不是,你不能改变它,你可以包裹列表Collections.unmodifiableList(...) ,它允许返回参数的超参数的参数的列表。 (它通过在插入尝试时抛出UnsupportedOperationException来避免types安全问题。)

正如@erickson所解释的,如果你真的想要引用原始列表,那么确保没有任何代码将任何东西插入到列表中,如果你想在原始的声明下再次使用它的话。 得到它的最简单的方法就是把它转换成一个普通的旧的非generics列表:

 List<BaseClass> baseList = (List)new ArrayList<SubClass>(); 

我不会推荐这个,如果你不知道列表会发生什么,并build议你改变任何代码需要列表来接受你的列表。

List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)

你正在做的事情是非常有用的,我发现我需要经常在我编写的代码中这样做。 一个示例用例:

假设我们有一个Foo接口,并且有一个zorking包,它有一个ZorkingFooManager ,它创build并pipe理包 – 私有ZorkingFoo implements Foo实例。 (一个非常常见的情况)

所以, ZorkingFooManager需要包含一个private Collection<ZorkingFoo> zorkingFoos但是它需要公开一个public Collection<Foo> getAllFoos()

大多数java程序员在实现getAllFoos()之前不会考虑三次分配一个新的ArrayList<Foo> ,并用zorkingFoos所有元素zorkingFoos并返回它。 我喜欢这样的想法:在全球数百万计算机上运行的java代码所消耗的所有时钟周期中,大约30%的时间周期没有任何作用,只是创build了无用的ArrayLists副本,这些副本在创build之后被微秒收集。

当然,解决这个问题的办法是向下收集这个集合。 这是做到这一点的最好方法:

 static <T,U extends T> List<T> downCastList( List<U> list ) { return castList( list ); } 

这将我们带到了castList()函数:

 static <T,E> List<T> castList( List<E> list ) { @SuppressWarnings( "unchecked" ) List<T> result = (List<T>)list; return result; } 

由于Java语言的颠倒,中间resultvariables是必需的:

  • return (List<T>)list; 产生“未经检查的强制转换”exception; 到现在为止还挺好; 但是之后:

  • @SuppressWarnings( "unchecked" ) return (List<T>)list; 是压制警告注释的非法使用。

因此,尽pipe在return语句中使用@SuppressWarnings并不是@SuppressWarnings ,但在赋值上使用它显然是好的,所以额外的“result”variables解决了这个问题。 (无论如何,它应该被编译器或者JIT优化掉。)

像这样的东西也应该工作:

 public static <T> List<T> convertListWithExtendableClasses( final List< ? extends T> originalList, final Class<T> clazz ) { final List<T> newList = new ArrayList<>(); for ( final T item : originalList ) { newList.add( item ); }// for return newList; } 

真的不知道为什么在Eclipse中需要clazz

下面是一个有用的代码片段。 它构造了一个新的数组列表,但是JVM对象的头创build是不重要的。

我看到其他答案不一定复杂。

 List<BaseClass> baselist = new ArrayList<>(sublist);