将List <subclass>强制转换为List <baseclass> </baseclass> </subclass>的最有效方法

时间:2011-02-22 18:08:18

标签: java inheritance casting

我有List<SubClass>我希望将其视为List<BaseClass>。看起来这不应该是一个问题,因为将SubClass转换为BaseClass非常容易,但是我的编译器抱怨演员阵容是不可能的。

那么,获取与List<BaseClass>相同的对象的引用的最佳方法是什么?

现在我只是制作一个新列表并复制旧列表:

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

但据我了解,必须创建一个全新的列表。如果可能的话,我想参考原始列表!

10 个答案:

答案 0 :(得分:155)

此类赋值的语法使用通配符:

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

重要的是要认识到List<SubClass> 可与List<BaseClass>互换。保留对List<SubClass>的引用的代码会希望列表中的每个项目都是SubClass。如果代码的另一部分将列表称为List<BaseClass>,则在插入BaseClassAnotherSubClass时,编译器不会抱怨。但这会导致第一段代码ClassCastException,假设列表中的所有内容都是SubClass

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

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

这是允许的,因为数组“知道”其元素的类型。如果有人试图在数组中存储不是SubClass实例的内容(通过bases引用),则会抛出运行时异常。

通用集合“知道”其组件类型;这些信息在编译时被“擦除”。因此,当发生无效存储时,它们无法引发运行时异常。相反,当从集合中读取值时,将在代码中的某个远距离,难以关联的点处引发ClassCastException。如果您注意有关类型安全性的编译器警告,您将在运行时避免这些类型错误。

答案 1 :(得分:35)

erickson已经解释了为什么你不能这样做,但这里有一些解决方案:

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

但是如果它不是并且您无法更改它,则可以使用Collections.unmodifiableList(...)包装列表,这允许返回参数参数的超类型的List。 (它通过在插入尝试时抛出UnsupportedOperationException来避免类型安全问题。)

答案 2 :(得分:10)

正如@erickson解释的那样,如果你真的想要一个对原始列表的引用,那么如果你想在原始声明下再次使用它,请确保没有代码向该列表插入任何内容。获得它的最简单方法是将其强制转换为普通的旧的非泛型列表:

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

如果您不知道列表会发生什么,我建议您更改列表中接受列表所需的任何代码。

答案 3 :(得分:2)

我错过了答案,在这里您只是使用两次强制转换将原始列表转换了。所以这里是为了完整性:

List<BaseClass> baseList = (List<BaseClass>)(List<?>)subList;

不复制任何内容,并且操作很快。但是,您在这里欺骗编译器,因此必须绝对确保不要以subList开始包含其他子类型的项的方式修改列表。当处理不可变列表时,这通常不是问题。

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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

假设我们有一个接口Foo,我们有一个zorking包,其中包含ZorkingFooManager,用于创建和管理package-private ZorkingFoo implements Foo的实例。 (一种非常常见的情况。)

因此,ZorkingFooManager需要包含private Collection<ZorkingFoo> zorkingFoos,但需要公开public Collection<Foo> getAllFoos()

大多数java程序员在实现getAllFoos()之前不会三思而后行,因为分配一个新的ArrayList<Foo>,用zorkingFoos中的所有元素填充它并返回它。我喜欢这样的想法,即在全球数百万台机器上运行的java代码消耗的所有时钟周期中,大约30%的时间没有做任何事情,只是创建了这些无用的ArrayLists副本,这些副本在创建后会被垃圾收集微秒。

这个问题的解决方案当然是向下转换集合。这是最好的方法:

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语言的异常,中间result变量是必需的:

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

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

因此,即使在@SuppressWarnings语句中使用return不是犹太教,但在分配上使用它显然很好,因此额外的“结果”变量可以解决这个问题。 (无论如何,它应该由编译器或JIT优化。)

答案 6 :(得分:0)

这样的事也应该有效:

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 ..

答案 7 :(得分:0)

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

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

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

答案 8 :(得分:0)

如何投射所有元素。它将创建一个新列表,但将引用旧列表中的原始对象。

List<BaseClass> convertedList = listOfSubClass.map(x -> (BaseClass)x).collect(Collectors.toList());

答案 9 :(得分:0)

这是使用泛型将子类列表转换为超类的完整代码。

传递子类类型的调用者方法

List<SubClass> subClassParam = new ArrayList<>();    
getElementDefinitionStatuses(subClassParam);

接受基类任何子类型的Callee方法

private static List<String> getElementDefinitionStatuses(List<? extends 
    BaseClass> baseClassVariableName) {
     return allElementStatuses;
    }
}