Rewrite Java code in Kotlin using Function Reference occurs SAM types conflict

无人久伴 提交于 2019-12-05 15:10:47

You can also using Function Reference Expression in Kotlin just like as Method Reference Expression in Java. for example, the combine function reference as KFunction3 in Kotlin as below:

val f: kotlin.reflect.KFunction3<String, Boolean, Int, Double> = this::combine

In java, Method Reference Expression can assign to any compatible Functional Interface, but you can't assign a Function Reference Expression to any Function types even if they are compatible, for example:

val f:io.reactivex.functions.Function3<String,Boolean,Int,Double> =this::combine
//                                                type mismatch error     ---^

Indeed, Kotlin using SAM Conversions to convert lambda/Function Reference Expression into an implementation of Java Functional Interface, which means Kotlin does something like as below:

fun test() = TODO()

val exector:Executor = TODO()

exector.execute(::test)

//::test compile to java code as below:
Runnable task = new Runnable(){
   public void run(){
      test();
   }
};

Why did Method Reference Expression can works fine in Java, but using Function Reference Expression is failed in Kotlin?

Base on the above, and you can see there are many overloaded combineLatest methods in . for example, the following two can't make Kotlin using Function Reference Expression correctly:

combineLatest(
       ObservableSource<out T1>,
       ObservableSource<out T2>,
       ObservableSource<out T3>, 
       Function3<T1, T2, T3, out R>
)

combineLatest(
       ObservableSource<out T1>,
       ObservableSource<out T2>,
       ObservableSource<out T3>, 
       ObservableSource<out T4>, 
       Function4<T1, T2, T3, T4, out R>
)

As I said Kotlin using SAM Conversions to convert a lambda/Function Reference Expression to a Java Functional Interface, but it can't assign to a Java Functional Interface directly.

The 4th parameter of the combineLatest method in Kotlin, the compiler can't infer its type at all, since the 4th parameter type can be io.reactivex.functions.Function3 or ObservableSource. because ObservableSource is also a Java Functional Interface.

When you meet SAM Conversion conflict like as this, you can using an adapter function that converts a lambda to a specific SAM type, for example:

typealias RxFunction3<T1, T2, T3, R> = io.reactivex.functions.Function3<T1,T2,T3,R>

val f: RxFunction3<String, Boolean, Int, Double> = RxFunction3{ s, b, i-> 1.0}

So you must using an adaption before using lambda/Function Reference Expression, for example:

typealias RxFunction3<T1, T2, T3, R> = io.reactivex.functions.Function3<T1,T2,T3,R>

fun <T1,T2,T3,R> KFunction3<T1,T2,T3,R>.toFunction3(): RxFunction3<T1, T2,T3,R> {
    return RxFunction3 { t1, t2, t3 -> invoke(t1, t2, t3) }
}

Then you can using Function Reference Expression as below, for example:

Observable.combineLatest(
        strings(),
        booleans(), 
        integers(), 
        //             v--- convert KFunction3 to RxFunction3 explicitly
        this::combine.toFunction3()
)
FRR

@holi-java Answer is great, it explains why this problem occurs, here I'm providing a quick fix for those who just want to get rid of the problem:

In rxJava2 use io.reactivex.rxkotlin.Observables instead of io.reactivex.Observable to combine your observables:

Observables.combineLatest(src1, src2, ::yourFunction)

This should work out of the box.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!