Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin SAM conversion doesn't work for function with two arguments

I'm learning Kotlin and trying to use LiveData with it. Since LiveData library is written in Java, Kotlin should support SAM conversions.

LiveData class has method observe with two arguments, Observer and LifecycleOwner:

void observe (LifecycleOwner owner, Observer<T> observer)

Both are interfaces with single method. When I use SAM lambdas for both arguments it works fine:

val liveData = MutableLiveData<Int>()
liveData.observe({ lifecycleOwner.lifecycle }, { invokeMyMethod(it) })

Also it works when I provide object and SAM lambda with specific type (Observer):

val liveData = MutableLiveData<Int>()
liveData.observe(lifecycleOwner, Observer { invokeMyMethod(it) })

However, when I'm trying to use object for one argument and SAM lambda without type for another argument:

val liveData = MutableLiveData<Int>()
liveData.observe(lifecycleOwner, { invokeMyMethod(it) })

it gives me compilation error. But why I can't write code like this? This is exactly what I want, compact and no boilerplate code. There is only one observe method in LiveData, why I have to specify the type for lambda?

Compiler produces such error:

Type mismatch. Required: Observer!>, Found: () → Unit

Small update, I've just checked this combination and it also doesn't work:

val liveData = MutableLiveData<Int>()
liveData.observe({ lifecycleOwner.lifecycle }, Observer { invokeMyMethod(it) })

So it works when both arguments are SAM lambdas, but doesn't work when one is SAM lambda and another is SAM lambda with specified type. In this case compiler also gives the same Type mismatch error.

like image 845
Andrei Mankevich Avatar asked Dec 01 '18 21:12

Andrei Mankevich


2 Answers

This is https://youtrack.jetbrains.com/issue/KT-14984. And according to that, it should finally be fixed in Kotlin 1.3, but the type inference changes required are opt-in for now and need to be enabled explicitly: https://discuss.kotlinlang.org/t/new-type-inference-in-kotlin-1-3-0-rc-190/9914/2

like image 187
Alexey Romanov Avatar answered Oct 20 '22 12:10

Alexey Romanov


Your observation seems to be correct, but I won't be able to answer you why.

options

We see that Kotlin sees either LifecycleOwner, Observer or () -> Lifecycle, (Int?) -> Unit. As for why you don't see one with each combination, only people who work on Kotlin would know!


However, I'll be able to give you a workaround.

Add this extension function to your project:

inline fun <T> LiveData<T>.observe(lifecycleOwner: LifecycleOwner, crossinline observer: (T?) -> Unit) {
    observe(lifecycleOwner, Observer { observer(it) })
}

And now it'll magically work (assuming your extension function is imported)!

fun blah() {
    liveData.observe(lifecycleOwner) { // <-- trailing lambda
        invokeMyMethod(it)
    }
}
like image 30
EpicPandaForce Avatar answered Oct 20 '22 10:10

EpicPandaForce