如何分配 List<?将 BaseClass> 扩展到 List<BaseClass>
How to assign List<? extends BaseClass> to List<BaseClass>
有
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
是由其他路由在运行时构建的,那部分代码具有从 aList
中获取 List<IData>
的功能
问题是 List<? extends IData> aList
是否指向 ArrayList<ChildClassA>()
或 ArrayList<ChildClassB>()
,
可以ListData<IData> outputList = (List<IData>) aList
吗?如下所示:
(似乎可以正常工作,但不确定是否有比转换更好的方法来分配泛型数组。)
编辑: List<IData> outputList
的输出仅供只读使用(不可变),上面没有 insert/delete,它只会迭代 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
不,不安全。
在转换之后,添加到应该只包含 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
完全相同的列表对象。
如果允许该转换,那么您将能够向 aList
添加不是 ChildClassA
实例的元素。
最好的解决方案在细节上。
如果问题是第三方库需要 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
tl;dr 使用 Collections#unmodifiableList
:
List<IData> outputList = Collections.unmodifiableList(aList);
有关此主题的更多信息,您可能希望熟悉 PECS 原则。
不可能,因为这两种类型不兼容。
一个List<BaseClass>
就是它所声明的,一个BaseClass
对象的列表。更准确地说,它做了两个保证:
- 从中检索到的对象可分配给
BaseClass
- 每个可分配给
BaseClass
的对象都可以添加到它(而不是其他对象)
A List<? extends BaseClass>
是一个更宽松的声明。准确的说,它根本就没有做二次保证。然而,不仅保证没有了,而且现在不可能向它添加项目,因为列表的确切泛型类型是未定义的。它甚至可能在运行时针对相同的列表声明(不是相同的列表对象)而改变。
因此,List<? extends BaseClass>
不能分配给 List<BaseClass>
,因为后者做出了第一个无法实现的保证。
实际上,考虑以下方法:
public List<BaseClass> makeList() {
// TODO implement method
return null;
}
如果有人实现此方法,返回 List<? extends BaseClass>
,使用此方法的客户端将无法向其添加项目,尽管其声明另有说明。
因此,这样的分配会导致编译错误。
要解决示例问题,可以将松散声明添加到方法中:
public List<? extends BaseClass> makeList() {
// TODO implement method
return null;
}
这将向每个客户端发出信号,表明从该方法返回的列表并不用于向其中添加项目。
现在让我们回到您的用例。在我看来,最合适的解决方法是改写函数
take[s] a List from the the aList.
目前似乎声明为
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);
有
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
是由其他路由在运行时构建的,那部分代码具有从 aList
List<IData>
的功能
问题是 List<? extends IData> aList
是否指向 ArrayList<ChildClassA>()
或 ArrayList<ChildClassB>()
,
可以ListData<IData> outputList = (List<IData>) aList
吗?如下所示:
(似乎可以正常工作,但不确定是否有比转换更好的方法来分配泛型数组。)
编辑: List<IData> outputList
的输出仅供只读使用(不可变),上面没有 insert/delete,它只会迭代 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
不,不安全。
在转换之后,添加到应该只包含 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
完全相同的列表对象。
如果允许该转换,那么您将能够向 aList
添加不是 ChildClassA
实例的元素。
最好的解决方案在细节上。
如果问题是第三方库需要 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
tl;dr 使用 Collections#unmodifiableList
:
List<IData> outputList = Collections.unmodifiableList(aList);
有关此主题的更多信息,您可能希望熟悉 PECS 原则。
不可能,因为这两种类型不兼容。
一个List<BaseClass>
就是它所声明的,一个BaseClass
对象的列表。更准确地说,它做了两个保证:
- 从中检索到的对象可分配给
BaseClass
- 每个可分配给
BaseClass
的对象都可以添加到它(而不是其他对象)
A List<? extends BaseClass>
是一个更宽松的声明。准确的说,它根本就没有做二次保证。然而,不仅保证没有了,而且现在不可能向它添加项目,因为列表的确切泛型类型是未定义的。它甚至可能在运行时针对相同的列表声明(不是相同的列表对象)而改变。
因此,List<? extends BaseClass>
不能分配给 List<BaseClass>
,因为后者做出了第一个无法实现的保证。
实际上,考虑以下方法:
public List<BaseClass> makeList() {
// TODO implement method
return null;
}
如果有人实现此方法,返回 List<? extends BaseClass>
,使用此方法的客户端将无法向其添加项目,尽管其声明另有说明。
因此,这样的分配会导致编译错误。
要解决示例问题,可以将松散声明添加到方法中:
public List<? extends BaseClass> makeList() {
// TODO implement method
return null;
}
这将向每个客户端发出信号,表明从该方法返回的列表并不用于向其中添加项目。
现在让我们回到您的用例。在我看来,最合适的解决方法是改写函数
take[s] a List from the the aList.
目前似乎声明为
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);