输入选角列表<ChildClass> 到列表<BaseClass>

Type Casting List<ChildClass> to List<BaseClass>

我有一个这样的 class 结构 :

public class BaseClass {

}

public class ChildClass extends BaseClass {

}

假设我有一个方法 getList returns List<BaseClass> :

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

List<BaseClass> b = getList();

我想使上述方法 getList() 通用,以便它可以 return 类型的对象 List<BaseClass>List<ChildClass> 基于某些条件和 仅将 returned 值分配给 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();

这些对我来说有点像 hack,可能是因为 Java 不支持直接转换。恐怕这里有一个问题我可能会遗漏。

尽管这些对我的用例来说效果很好,但我应该担心这种方法有什么影响吗?

写这样的代码安全吗?

写代码的方式不好吗?如果是,那么应该如何处理?

is there any implication with this approach that I should worry about?

对于一般情况,是的:您可以调用 List<ChildClass> list = getList();,最后得到一个列表,其中包含不是 ChildClass.

实例的内容

从根本上说,您不能做任何导致从方法安全返回不同类型的事情。你能做的最好的就是通配符:

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

因为该列表中的所有内容要么是 BaseClass(某种风格),要么是 null。

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

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

请注意,如果您使用的是类似 Guava 的 ImmutableList,此 "copy" 可能是免费的:

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

尽管名称,[​​=21=] returns 参数,如果它是 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 要么没有在其他地方使用,要么不会被除转换代码以外的任何其他内容修改。