我目前正在审核将应用升级为使用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之前未显示此错误。为什么会出现此问题?
答案 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>
。我不打算判断每个版本中哪种行为是正确的,正式的正确行为有时与直觉相矛盾,但是当使用如上所示的显式类型时,它应该适用于所有编译器的两个版本。