使用函数引用重写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中KFunction3
的combine
函数参考如下:
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.Function3
或ObservableSource
,所以在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() )