【问题标题】:Type Casting List<ChildClass> to List<BaseClass>类型转换 List<ChildClass> 到 List<BaseClass>
【发布时间】:2019-12-25 00:59:35
【问题描述】:

我有一个这样的类结构:

public class BaseClass {

}

public class ChildClass extends BaseClass {

}

假设我有一个返回 List&lt;BaseClass&gt; 的方法 getList :

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

List<BaseClass> b = getList();

我想让上述方法 getList() 通用,以便它可以根据某些条件返回 List&lt;BaseClass&gt;List&lt;ChildClass&gt; 类型的对象,并将返回的值分配给 @987654326 类型的对象仅限@。这样做的原因是我正在处理一些遗留代码,其中对象的类型为 List&lt;BaseClass&gt;,我想避免将它们重构为 List&lt;? extends BaseClass&gt; 之类的东西,因为这会阻止这些列表对象上的 add() 等现有操作。

我了解List&lt;BaseClass&gt;List&lt;ChildClass&gt; 不是协变的。

但是,使用以下技术,我可以将 List&lt;ChildClass&gt; 转换为 List&lt;BaseClass&gt;,但我很困惑它是如何在幕后发生的,以及这样做是否正确。

使用显式转换:

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 不支持直接转换。恐怕这里有一个我可能会遗漏的问题。

虽然这些对我的用例来说很好用,但我应该担心这种方法有什么含义吗?

写这样的代码安全吗?

这是一种糟糕的代码编写方式吗?如果是,那么应该如何处理?

【问题讨论】:

  • “使用泛型”(和“使用通配符”):在这两种情况下,您都返回一个空列表,因此您可以简单地将方法主体替换为 return new ArrayList&lt;&gt;();。两个列表实例之间没有区别。
  • 这只是为了说明。实际上,这些列表将包含定义列表类型的对象。
  • 列表元素从何而来?
  • 我更新了代码。希望这能让它更清楚。不管它们来自哪里,最后,我们都只会有任何一种类型的列表。
  • 因为这会阻止现有的操作,比如对这些列表对象的 add() 操作”——这正是泛型的意义所在。当您的 List&lt;? extends BaseClass&gt; 实际上是 List&lt;ChildClass&gt; 时,您不得添加不是 ChildClassBaseClass 实例。正确的没有“未经检查”警告的通用代码将不允许您这样做。最终将引用从List&lt;ChildClass&gt; 转换为List&lt;BaseClass&gt; 的任何类型转换序列都感觉很老套,因为它们 hacky。这条规则只有一个例外,List&lt;BaseClass&gt; a = Collections.unmodifiableList(b);猜猜为什么……

标签: java generics collections casting wildcard


【解决方案1】:

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

对于一般情况,是的:您可以调用List&lt;ChildClass&gt; list = getList();,并最终得到一个列表,其中包含不是ChildClass 实例的内容。

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

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

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

然后您可以通过复制将其安全地转换为List&lt;BaseClass&gt;

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

请注意,如果您使用像 Guava 的 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 不会在其他地方使用,或者不会被转换代码以外的任何东西修改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-21
    • 1970-01-01
    • 2013-06-02
    相关资源
    最近更新 更多