6

class BaseClass implements IData ();
class ChildClassA() extends BaseClass;
class ChildClassB() extends BaseClass;

因为不能做

List<BaseClass> aList = new ArrayList<ChildClassA>()

所以有一个

List<? extends IData> aList 
for pointint to either 
    ArrayList<ChildClassA>(),  
or 
    ArrayList<ChildClassB>()

aList由其他路由在运行时构建的,并且该部分代码具有List<IData>aList

问题是如果List<? extends IData> aList is 指向ArrayList<ChildClassA>() or ArrayList<ChildClassB>(),

可以ListData<IData> outputList = (List<IData>) aList吗?如下所示:

(似乎它正在工作,但不确定是否有更好的方法来分配泛型数组而不是强制转换。)

编辑:的输出List<IData> outputList 是只读的(不可变的),没有插入/删除,它只会迭代 IData 以对 IData 的真正含义做出反应。

List<? extends IData> aList =  new ArrayList<ChildClassA>();
ListData<IData> outputList = (List<IData>)aList

List<? extends IData> aList =  new ArrayList<ChildClassB>();
ListData<IData> outputList = (List<IData>)aList
4

2 回答 2

3

不,这是不安全的。

在该转换之后,添加到应该只包含ChildClassA类型化元素、其他子类型ChildClassB类型的元素的列表中是合法的,反之亦然。

我们可以稍微简化一下您的代码,以更清楚地说明为什么不允许这样做:

List<ChildClassA> aList =  new ArrayList<ChildClassA>();
aList.add(a1);
aList.add(a2);
//...
List<IData> iDataList = (List<IData>) aList;
iDataList.add(b1);
iDataList.add(b2);
//...
for (ChildClassA a : aList) {
   // a some point a is going to be assigned b1 or b2 and they results in cast
   // exception.
}

请注意,iDataList它引用了与 相同的列表对象aList。如果允许该演员,那么您将能够向aListChildClassA实例添加元素。

最好的解决方案是细节。

如果问题是第三方库需要List<IData>类型化的引用,并且只要它仅用于阅读,您就可以使用以下方式返回的不可修改代理Collections.unmodifiableList

import java.util.Collections;
//...
final List<ChildClassA> aList = new ArrayList<>();
//... add stuff to aList
final List<IData> readOnlyIDataList = Collections.unmodifiableList(aList); 
//... read only access operations readOnlyIDataList 
于 2018-05-23T19:45:28.690 回答
3

tl;博士使用Collections#unmodifiableList

List<IData> outputList = Collections.unmodifiableList(aList);

有关此主题的更多信息,您可能需要熟悉PECS原理。


这是不可能的,因为这两种类型不兼容。

AList<BaseClass>就是它所声明的,一个BaseClass对象列表。更准确地说,它做了两个保证:

  • 从中检索到的对象可分配给BaseClass
  • 每个可分配的对象BaseClass都可以添加到其中(不能添加其他对象)

AList<? extends BaseClass>是一个更宽松的声明。准确地说,它根本不做二次保证。然而,不仅保证消失了,而且现在不可能向其中添加项目,因为列表的确切泛型类型是未定义的。它甚至可能在运行时更改为相同的列表声明(不是相同的列表对象)。

因此, aList<? extends BaseClass>不能分配给 a List<BaseClass>,因为后者保证了第一个无法实现。


实际上,请考虑以下方法:

public List<BaseClass> makeList() {
    // TODO implement method
    return null;
}

如果有人实现此方法并返回 a List<? extends BaseClass>,则使用此方法的客户端将无法向其添加项目,尽管其声明另有说明。

因此,这样的赋值会导致编译错误。

要解决示例问题,可以将松散声明添加到方法中:

public List<? extends BaseClass> makeList() {
    // TODO implement method
    return null;
}

这将向每个客户端发出信号,从该方法返回的列表不用于向其中添加项目。


现在让我们回到您的用例。在我看来,最合适的解决方法是改写以下功能

从 aList 中获取 [s] 一个列表。

看起来它目前被宣布为

public void useList(List<BaseClass> list);

但由于它不会将项目添加到列表中,因此应将其声明为

public void useList(List<? extends BaseClass> list);

但是,如果该方法是当前不可更改的 API 的一部分,您仍然可以执行以下操作:

List<? extends BaseClass> list;
....
List<BaseClass> tmp = Collections.unmodifiableList(list);
useList(tmp);
于 2018-05-23T19:45:59.947 回答