类型转换列表<ChildClass>到列表<BaseClass>

时间:2019-08-19 19:40:11

标签: java generics collections casting wildcard

我有一个像这样的类结构:

public class BaseClass {

}

public class ChildClass extends BaseClass {

}

让我们说我有一个方法getList返回List<BaseClass>

public List<BaseClass> getList() {
     List<BaseClass> b = new ArrayList<>();
     return b;
}

List<BaseClass> b = getList();

我想使上述方法getList()通用,以便它可以根据某些条件返回List<BaseClass>List<ChildClass>类型的对象,并将返回值分配给对象类型List<BaseClass> 。原因是我正在处理一些旧代码,其中对象的类型为List<BaseClass>,并且我想避免将它们重构为List<? extends BaseClass>之类的内容,因为这会阻塞现有的add()之类的操作。列出对象。

我知道List<BaseClass>List<ChildClass>不是协变的。

但是,使用下面的技术,我可以将List<ChildClass>投射到List<BaseClass>,但我对它在幕后的发生方式以及是否这样做是正确的感到困惑。

使用显式强制转换:

List<BaseClass> b = new ArrayList<>();
List<ChildClass> c = new ArrayList<>();
b = c; // gives compilation error
b = (List<BaseClass>) c; // gives compilation error
b = (List<BaseClass>)(List<?>) c; // this works

使用泛型:


public List<ChildClass> getChildClassList() {
   // code to fetch/generate list and return
}

public List<BaseClass> getBaseClassList() {
   // code to fetch/generate list and return
}

public static <T extends BaseClass> List<T> getList() {
    if(something) {
        List<ChildClass> c = getChildClassList();
        return (List<T>) c; // this cast is required here.
    } else {
        List<BaseClass> b = getBaseClassList();
        return (List<T>) b;
    }
}

List<BaseClass> = getList(); // This works perfectly fine but,
                             // I dont understand how casting is happening here.
                             // Also with cast to List<T> inside getList how is the final return type determined?

使用通配符:


public List<ChildClass> getChildClassList() {
   // code to fetch/generate list and return
}

public List<BaseClass> getBaseClassList() {
   // code to fetch/generate list and return
}

public List<? extends BaseClass> getList() {
    if(something) {
        List<Child> c = getChildClassList();
        return c;
    } else {
        List<Base> b = getBaseClassList();
        return b;
    }
}

List<BaseClass> = getList(); // Gives compilation error saying incompatible types

// Casting this, however, works fine
List<BaseClass> = (List<BaseClass>) getList();

这些感觉对我来说有点像黑客,原因可能是Java不支持直接投射。恐怕这里有一个我可能会错过的收获。

尽管这些对于我的用例来说都可以正常工作,但是我应该担心这种方法有什么暗示吗?

编写这样的代码安全吗?

编写代码是否不好?如果是,那么应该如何处理?

1 个答案:

答案 0 :(得分:4)

  

我应该担心这种方法有什么影响吗?

对于一般情况,是的:您可以调用List<ChildClass> list = getList();,最后得到一个列表,列表中列出的内容不是ChildClass的实例。

从根本上讲,您无法做任何导致从方法安全返回不同类型的操作。最好的办法是使用通配符:

List<? extends BaseClass> list = getList();

因为该列表中的所有内容都是BaseClass(有些味道)或为空。

然后您可以通过复制将其安全地转换为List<BaseClass>

List<BaseClass> list2 = new ArrayList<>(list);

请注意,如果您使用的是类似番石榴的ImmutableList,则此“副本”可能是免费的:

ImmutableList<BaseClass> list2 = ImmutableList.copyOf(list);

尽管名称copyOf返回参数cast,如果它是ImmutableList的实例,因为这是一个安全的协变类型。


就将list转换为ChildClass而言:在编译时您无能为力。您必须将其转换为运行时检查:

// Throw an exception or something if this is false.
boolean allAreChildClass = list.stream().allMatch(e -> e == null || e instanceof ChildClass);

然后复制列表,进行投射。

List<ChildClass> list3 =
    list.stream().map(ChildClass.class::cast).collect(Collectors.toList());

或者,如果可以确定list不会在其他任何地方使用,则可以简单地进行以下铸造:

List<ChildClass> list3 = (List<ChildClass>) (List<?>) list;

但是您必须真的确保list不在其他地方使用,否则除铸造代码外不会被其他任何东西修改。