不兼容的类型,找到List <capture <? extends =“”model =“”>&gt;仅在Java 8中

时间:2018-01-09 21:54:43

标签: java android android-studio java-8 rx-java2

我目前正在审核将应用升级为使用Java 8的Android应用程序的更改。

在审核期间,我注意到以下代码被加下划线为错误:

List<MappableTask> tasks = Observable.fromIterable(taskMapHoldingPresenter.getMappableTasks())
        .filter(isMappableTask)
        .toList()
        .blockingGet();

现在显示以下内容:

Incompatible Types.
Required: List<MappableTask>
Found: List<capture<? extends MappableTask>>

getMappableTasks()方法在接口中定义如下:

List<? extends MappableTask> getMappableTasks();

请注意,MappableTask是由各种模型类实现的接口。

List<MappableTask> list = taskMapHoldingPresenter.getMappableTasks();如何无效?

此代码按预期构建。如上所述,在升级应用程序以使用Java 8之前未显示此错误。为什么会出现此问题?

2 个答案:

答案 0 :(得分:4)

您的接口声明允许getMappableTasks方法返回任何MappableTask子类的列表。假设MappableAsyncTask扩展MappableTask,理论上您的界面可以返回List<MappableAsyncTask>

class MappableAsyncTask extends MappableTask {
}

class AsyncPresenter implements TaskMapHoldingPresenter {
    @Override
    public List<? extends MappableTask> getMappableTasks() {
        List<MappableAsyncTask> result = new ArrayList<>();
        result.add(new MappableAsyncTask());
        return result;
    }
}

List<MappableAsyncTask>不是List<MappableTask>的子类,因此您无法将getMappableTasks结果分配给tasks变量。

List<MappableTask> tasks = asyncPresenter.getMappableTasks(); // wrong

这种限制存在是有原因的。如果可以进行此分配,您可以轻松地将MappableTask实例添加到tasks列表中,从而打破实际的列表实施合同。请记住,getMappableTasks方法返回的实际列表只能包含MappableAsyncTask

的实例
List<MappableTask> tasks = asyncPresenter.getMappableTasks(); // wrong
tasks.add(new MappableTask()); // wrong

更多信息here

答案 1 :(得分:0)

您可以使用

List<MappableTask> tasks
    = Observable.<MappableTask>fromIterable(taskMapHoldingPresenter.getMappableTasks())
        .filter(isMappableTask)
        .toList()
        .blockingGet();

正如this answer正确解释的那样,您不能简单地将List<? extends MappableTask>分配给List<MappableTask>,因为这会破坏类型安全性,即允许插入错误的元素。

所以分配

List<MappableTask> list = taskMapHoldingPresenter.getMappableTasks();

永远无效,但请注意,如果您可以排除此类插入,则可以使用超类型的集合,即

List<MappableTask> list // just a lightweight wrapper
    = Collections.unmodifiableList(taskMapHoldingPresenter.getMappableTasks());

同样,创建副本有效

List<MappableTask> list = new ArrayList<>(taskMapHoldingPresenter.getMappableTasks());

因为它无法修改原始列表。

您创建的Observable只会读取最终会创建新列表的元素和链接操作,因此如果新列表的类型为List<MappableTask>则没有问题。

但是你的编译器为Observable<? extends MappableTask>的结果推断了类型fromIterable,它通过链传播,最后得到结果类型
List<? extends MappableTask>。通过插入显式元素类型<MappableTask>,我们创建了一个Observable<MappableTask>,结果类型将是List<MappableTask>。这是有效的,因为fromIterable接受迭代返回更具体类型的元素。

由于Java 7和Java 8之间的类型推断已经改变,因此先前的编译器可能推断Observable<MappableTask>并且当前推断Observable<? extends MappableTask>。我不打算判断每个版本中哪种行为是正确的,正式的正确行为有时与直觉相矛盾,但是当使用如上所示的显式类型时,它应该适用于所有编译器的两个版本。