列表回调参数上的 Kotlin 通配符捕获

Kotlin Wildcard Capture on List Callback Parameter

Java:

public class JavaClass implements ModelController.Callback {

    @Override
    public void onModelsLoaded(@NonNull List<? extends Model> models) {
        doSomething(models);
    }

    private void doSomething(List<Model> models) { }

}

科特林:

class ModelController {
    var callback = WeakReference<Callback>(null)

    interface Callback {
        fun onModelsLoaded(models: List<Model>)
    }

    fun someFunction() {
        callback.get().onModelsLoaded(ArrayList<Model>())
    }
}

interface Model {

}

没有?在 Java onModelsLoaded 方法中扩展模型,覆盖与 Kotlin 中的接口不匹配。有了它,我得到以下错误:

doSomething(<java.util.List<com.yada.Model>) cannot be applied to (java.util.List<capture<? extends com.yada.Model>>)

为什么需要通配符捕获,为什么不允许它用于非通配符方法?

问题源于 Kotlin 集合 variant,并且 Java 仅具有通过通配符实现的使用位置差异(捕获与通配符有关,但不完全是 ? extends ... 语法本身)。

在 Kotlin 中我们说 List<Model> 表示 "read-only list of Model or subtypes of Model",当我们在 Java 中表示相同时表示 "mutable list of exactly Model and nothing else"。大致意思是 Kotlin 的 List<Model> 的意思,在 Java 中我们必须说 List<? extends Model>,这就是为什么要使覆盖起作用,您必须将通配符添加到 Java 代码中.

现在,你的 doSomething 写成 Java 并且说它想要 "a list of exactly Model",当你给它 "a list of Model or its subtypes" 时, Java编译器抱怨,因为它可能很危险:doSomething 可能会尝试做一些不合法的事情,例如 ModelImpl,因为它认为它正在处理 Model 的列表.

截至目前(Kotlin Beat 2),您有两个选择:

  • 在您的 Kotlin 代码中使用 MutableList<Model> - 这正是 Java 的 List<Model>
  • 的意思
  • 定义 doSomething 以便它需要 List<? extends Model>,这就是您当前的 Kotlin 代码的含义。

在 Kotlin 的下一次更新中,我们将在类型上添加一个注释,以便更清晰地解决此问题。

解决捕获问题

你可以这样做:

void doSomething(List<Model> models) {
    new ArrayList(models)
}