如何分配List <! - ?扩展BaseClass - >到List <baseclass>

时间:2018-05-23 19:20:36

标签: java generics list-template

<!DOCTYPE html>
<html>

<head>
  <style>
    table {
      border-collapse: collapse;
    }
    
    th,
    td {
      border: 1px solid black;
      padding: 5px;
      text-align: left;
    }
  </style>
</head>

<body>


  <table>

    <tr>
      <th rowspan="4">Henkilöstöt</th>
      <th colspan="3">koulutus</th>
    </tr>
    <tr>
      <td rowspan="3">Muu</td>
      <td rowspan="3">Ammattiiiii</td>
      <td rowspan="3">korkeakoulu</td>
    </tr>
    <tr>
    </tr>
    <tr>
    </tr>

    <tr>
      <td colspan="4">55577855</td>
    </tr>
    <tr>
      <td colspan="4">ikä</td>
    </tr>

  </table>




</body>

</html>

既然无法做到

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还是ArrayList<ChildClassA>()

可以ArrayList<ChildClassB>()吗?如下所示:

(似乎它有效,但不确定是否有更好的方法来分配除了强制转换之外的泛型数组。)

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

List<IData> outputList

2 个答案:

答案 0 :(得分: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 

答案 1 :(得分:3)

tl; dr 使用Collections#unmodifiableList

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

有关此主题的更多信息,您可能希望熟悉PECS原则。

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

List<BaseClass>正是它所声明的内容,是BaseClass个对象的列表。更确切地说,它有两个保证:

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

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;
}

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

现在让我们回到您的用例。在我看来,最合适的解决方法是重新定义

的功能
  

从aList中获取一个List。

因为它似乎现在被宣布为

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);