使用函数引用重写Kotlin中的Java代码会发生SAM类型冲突

我有一个示例Java代码使用方法引用,我想重写到Kotlin。 Java版本使用方法参考,解决方案简短明了。 但另一方面,我不能在Kotlin中使用方法引用。 我设法编写的唯一版本是下面介绍的一个。 看起来像Function3 { s: String, b: Boolean, i: Int -> combine(s, b, i) }可以用更简洁的方式编写(如果可能的话,方法引用将是完美的)。

我是Kotlin新手,所以我会感激任何线索。

Java的

 import io.reactivex.Observable; public class TestJava { Observable<String> strings() { return Observable.just("test"); } Observable<Boolean> booleans() { return Observable.just(true); } Observable<Integer> integers() { return Observable.just(1); } void test() { Observable.combineLatest(strings(), booleans(), integers(), this::combine); } double combine(String s, boolean b, int i) { return 1.0; } } 

科特林

 import io.reactivex.Observable import io.reactivex.functions.Function3 class TestKotlin { fun strings(): Observable<String> { return Observable.just("test") } fun booleans(): Observable<Boolean> { return Observable.just(true) } fun integers(): Observable<Int> { return Observable.just(1) } fun test() { Observable.combineLatest(strings(), booleans(), integers(), Function3 { s: String, b: Boolean, i: Int -> combine(s, b, i) }) } fun combine(s: String, b: Boolean, i: Int): Double { return 1.0 } } 

编辑

以下Kotlin中的方法引用给出了一个错误。

 fun test() { Observable.combineLatest(strings(), booleans(), integers(), this::combine) } fun test() { Observable.combineLatest(strings(), booleans(), integers(), TestKotlin::combine) } 

下面的函数都不能用所提供的参数调用:@CheckReturnValue @SchedulerSupport public final fun combineLatest(p0:((Observer) – > Unit)!, p1 :((Observer) – > Unit)!, p2:(观察者) – >单位)!,p3:((???,???) – >))!):可观察<(??? ..))! 定义在io.reactivex.Observable @CheckReturnValue @SchedulerSupport public open fun combineLatest(p0:ObservableSource!,p1:ObservableSource!,p2:ObservableSource!,p3:io.reactivex.functions.Function3!):Observable <(??? ???)>! 定义在io.reactivex.Observable @CheckReturnValue @SchedulerSupport public open fun combineLatest(p0:Function !, out(??? .. ???)> !, p1:Int,vararg p2:ObservableSource!):Observable <(? ?? .. ???)>! 在io.reactivex.Observable中定义

编辑2

RxKotlin解决了答案中提到的问题。

 import io.reactivex.rxkotlin.Observables class TestKotlin { fun test() { Observables.combineLatest(strings(), booleans(), integers(), this::combine) } } 

您也可以像使用Java中的方法引用表达式一样在Kotlin中使用函数引用表达式 。 例如Kotlin中KFunction3combine函数参考如下:

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

在java中,方法引用表达式可以分配给任何兼容的 功能接口 ,但是即使它们是兼容的 ,也不能将函数引用表达式分配给任何函数类型,例如:

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

实际上,Kotlin使用SAM转换将lambda / Function Reference Expression转换为Java Functional Interface的实现,这意味着Kotlin的功能如下:

 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(); } }; 

为什么方法引用表达式可以在Java中正常工作,但是在Kotlin中使用函数引用表达式失败?

基于上述,你可以看到在rx-java2中有很多重载的combineLatest方法。 例如,以下两个不能使Kotlin正确使用函数引用表达式 :

 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> ) 

正如我所说的使用SAM转换的 Kotlin将lambda / Function引用表达式转换为Java Functional Interface ,但不能直接分配给Java Functional Interface 。

由于第四个参数类型可以是io.reactivex.functions.Function3ObservableSource ,所以在Kotlin中combineLatest方法的第四个参数,编译器根本无法推断它的类型。 因为ObservableSource也是一个Java 功能接口 。

当像这样遇到SAM转换冲突时,可以使用将lambda转换为特定SAM类型的适配器函数 ,例如:

 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} 

所以你必须在使用lambda / Function Reference Expression之前使用适配,例如:

 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) } } 

然后你可以使用下面的函数参考表达式 ,例如:

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