通过 List<subclass><superclass> 从 List<?> 向上转型为 List

问题描述 投票:0回答:3

我有一个 A 类,B 类扩展了 A

在另一个C类中我有一个字段

private List<B> listB;

现在,由于某些不寻常的原因,我必须在 C 中实现这个方法

public List<A> getList();

我尝试通过 List

<A>
强制将 listB 字段向上转换为 List
<?>
来实现此目的:

public List<A> getList(){
    return (List<A>)(List<?>)listB;
}

客户应该做的事情

List<A> list = getList();
for(A a:list){
    //do something with a
}

我做了一些测试,它似乎工作正常,但老实说,我不确定所有可能的影响。

这个解决方案正确吗?这是最好的解决方案吗?

感谢您的回答。

java generics collections wildcard
3个回答
16
投票

不,这通常不是类型安全的。客户不应该能够做

List<A> list = getList();

因为否则他们可以写

list.add(new C()); // Where C extends A

然后,假设每个元素都与

List<B>
兼容,那么将列表了解为
B
的原始代码在尝试使用它时将会出现问题。

您可以有效地包装原始列表以使其只读,或者使

getList
返回
List<? extends A>
,这意味着客户端无论如何都无法向其中添加项目。

如果您使用的列表实现是不可修改的,那么它实际上不会导致问题 - 但我个人仍然会尽可能避免它。


3
投票

这样做的问题是,客户端可能会无意中将

A
对象插入到实际上只是更具体的
B
对象的列表中:

c.getList().add(new A());

当您的代码尝试从列表中获取一个对象(假设该对象是

B
)时,这将导致各种损坏,但事实并非如此。

如果您的唯一目标是让客户迭代列表,最好给出一个

Iterable<A>

public Iterable<A> getAs() { return this.theListOfAs; }

通过这个

Iterable
,人们只能检查和删除元素,而不能添加它们。

如果您也想禁用删除,请将

List
Iterable
包装在您自己的实现中,在调用
UnsupportedOperationException
时抛出
remove()


0
投票

只需这样做

List<A> getList() {
    return List.copyOf(listB);
}

如果你想要可变性,你也可以返回

new ArrayList(listB)

正如其他回复所提到的,向上转换集合是不安全的,因为调用者可能会通过将错误的元素放入其中来破坏它。幸运的是,类型系统阻止您这样做,您别无选择,只能使用这些类型良好的方法复制集合,因此它可以正常工作。

也适用于地图。

© www.soinside.com 2019 - 2024. All rights reserved.