繁体   English   中英

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

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

我有一个示例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)
    }

}

您也可以在Kotlin中使用函数引用表达式 ,就像Java中的方法引用表达式一样。 例如,Kotlin中的combine函数引用为KFunction3 ,如下所示:

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中使用函数参考表达式失败了?

基于上面的内容,您可以看到有许多重载的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>
)

正如我所说,Kotlin使用SAM转换将lambda / 函数引用表达式转换为Java 功能接口 ,但它不能直接分配给Java 功能接口

Kotlin中combineLatest方法的第4个参数,编译器根本无法推断它的类型,因为第4个参数类型可以是io.reactivex.functions.Function3ObservableSource 因为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()
)

@ holi-java答案很棒,它解释了为什么会出现这个问题,在这里我为那些只想摆脱问题的人提供快速解决方案:

rxJava2使用io.reactivex.rxkotlin.Observables而不是io.reactivex.Observable来组合您的observable:

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

这应该是开箱即用的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM