Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin Wildcard Capture on List Callback Parameter

Tags:

kotlin

Java:

public class JavaClass implements ModelController.Callback {

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

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

}

Kotlin:

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

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

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

interface Model {

}

Without the ? extends Model in the Java onModelsLoaded method, the override doesn’t match the interface made in Kotlin. With it, I get the following error:

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

Why is the wildcard capture required and why doesn't it allow it to be used against the non-wildcard method?

like image 669
Jeremy Lyman Avatar asked Nov 23 '15 17:11

Jeremy Lyman


1 Answers

The issue stems from Kotlin collections being variant, and Java only having use-site variance which is implemented though wildcards (capture is something connected to wildcards but not exactly the ? extends ... syntax itself).

When in Kotlin we say List<Model> it means "read-only list of Model or subtypes of Model", when we say the same in Java it means "mutable list of exactly Model and nothing else". To mean roughly what Kotlin's List<Model> means, in Java we have to say List<? extends Model>, this is why for the override to work you have to add the wildcard into the Java code.

Now, your doSomething is written in Java and says that it wants "a list of exactly Model", and when you are giving it "a list of Model or its subtypes", the Java compiler complains, because it can be dangerous: doSomething might try to do something that is not legitimate for a list of, say, ModelImpl, because it thinks it's working on a list of Model.

As of now (Kotlin Beat 2), you have two options:

  • use MutableList<Model> in your Kotlin code - this mean exactly what Java's List<Model> means, or
  • define doSomething so that it takes List<? extends Model>, which is what your current Kotlin code means.

In the next update of Kotlin we'll add an annotation on types to facilitate a somewhat cleaner workaround for this problem.

like image 185
Andrey Breslav Avatar answered Nov 19 '22 19:11

Andrey Breslav