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

Muk*_*del 2 java generics collections casting wildcard

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

public class BaseClass {

}

public class ChildClass extends BaseClass {

}
Run Code Online (Sandbox Code Playgroud)

可以说我有一个方法getList返回List<BaseClass>

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

List<BaseClass> b = getList();
Run Code Online (Sandbox Code Playgroud)

我想使上述方法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
Run Code Online (Sandbox Code Playgroud)

使用泛型:


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?
Run Code Online (Sandbox Code Playgroud)

使用通配符:


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();
Run Code Online (Sandbox Code Playgroud)

在我看来,这些感觉有点像黑客,因为Java不支持直接投射。恐怕这里可能没有我想要的东西。

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

编写这样的代码安全吗?

这是编写代码的不好方法吗?如果是,那应该如何处理?

And*_*ner 5

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

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

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

List<? extends BaseClass> list = getList();
Run Code Online (Sandbox Code Playgroud)

因为该列表中的所有内容都会是BaseClass(某种意义上的)或为null。

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

List<BaseClass> list2 = new ArrayList<>(list);
Run Code Online (Sandbox Code Playgroud)

请注意,如果您使用的是Guava's之类的东西ImmutableList,此“副本”可能是免费的:

ImmutableList<BaseClass> list2 = ImmutableList.copyOf(list);
Run Code Online (Sandbox Code Playgroud)

尽管有名称,copyOf但如果参数是cast的实例,则返回参数cast ImmutableList,因为这是安全的协变强制转换。


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

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

然后复制列表,执行转换。

List<ChildClass> list3 =
    list.stream().map(ChildClass.class::cast).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

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

List<ChildClass> list3 = (List<ChildClass>) (List<?>) list;
Run Code Online (Sandbox Code Playgroud)

但是您必须真正确定它list不会在其他地方使用,或者除铸造代码外不会被其他任何东西修改。