使用函数引用在 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;
}
}
Kotlin
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)
}
None of the following functions can be called with the arguments
supplied: @CheckReturnValue @SchedulerSupport public final fun combineLatest(p0: ((Observer) -> Unit)!, p1: ((Observer) -> Unit)!, p2:
((Observer) -> Unit)!, p3: ((???, ???, ???) -> ???)!):
Observable<(???..???)>! defined in io.reactivex.Observable
@CheckReturnValue @SchedulerSupport public open fun combineLatest(p0: ObservableSource!, p1: ObservableSource!, p2:
ObservableSource!, p3: io.reactivex.functions.Function3!):
Observable<(???..???)>! defined in io.reactivex.Observable
@CheckReturnValue @SchedulerSupport public open fun combineLatest(p0: Function!, out
(???..???)>!, p1: Int, vararg p2: ObservableSource!):
Observable<(???..???)>! defined in io.reactivex.Observable
编辑 2
RxKotlin 解决了答案中提到的问题。
import io.reactivex.rxkotlin.Observables
class TestKotlin {
fun test() {
Observables.combineLatest(strings(), booleans(), integers(), this::combine)
}
}
您也可以在 Kotlin 中使用 Function Reference Expression,就像 Java 中的方法引用表达式一样。例如,combine
函数在 Kotlin 中引用为 KFunction3
,如下所示:
val f: kotlin.reflect.KFunction3<String, Boolean, Int, Double> = this::combine
在 java 中,方法引用表达式可以分配给任何 compatible Functional Interface,但不能将 Function Reference Expression 分配给任何 Function Reference Expression函数类型,即使它们 兼容 ,例如:
val f:io.reactivex.functions.Function3<String,Boolean,Int,Double> =this::combine
// type mismatch error ---^
确实,Kotlin 使用 SAM Conversions 将 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中使用Function Reference Expression却失败了?
根据上面,可以看到rx-java2中有很多重载的combineLatest
方法。例如,以下两个无法使 Kotlin 正确使用 Function Reference Expression:
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 Conversions 将 lambda/Function Reference Expression 转换为 Java Functional Interface,但它不能直接分配给 Java Functional Interface。
Kotlin中combineLatest
方法的第4个参数,编译器根本无法推断出它的类型,因为第4个参数类型可以是io.reactivex.functions.Function3
或ObservableSource
。因为 ObservableSource
也是一个 Java Functional Interface.
当你遇到像这样的SAM Conversion冲突时,你可以使用适配器函数将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) }
}
然后你可以使用Function Reference Expression如下,例如:
Observable.combineLatest(
strings(),
booleans(),
integers(),
// v--- convert KFunction3 to RxFunction3 explicitly
this::combine.toFunction3()
)
@holi-java 答案很好,解释了为什么会出现这个问题,这里我为那些只想摆脱这个问题的人提供一个快速修复:
在 rxJava2
中使用 io.reactivex.rxkotlin.Observables
而不是 io.reactivex.Observable
来组合您的观察值:
Observables.combineLatest(src1, src2, ::yourFunction)
这应该开箱即用。
我有一个示例 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;
}
}
Kotlin
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)
}
None of the following functions can be called with the arguments supplied: @CheckReturnValue @SchedulerSupport public final fun combineLatest(p0: ((Observer) -> Unit)!, p1: ((Observer) -> Unit)!, p2: ((Observer) -> Unit)!, p3: ((???, ???, ???) -> ???)!): Observable<(???..???)>! defined in io.reactivex.Observable @CheckReturnValue @SchedulerSupport public open fun combineLatest(p0: ObservableSource!, p1: ObservableSource!, p2: ObservableSource!, p3: io.reactivex.functions.Function3!): Observable<(???..???)>! defined in io.reactivex.Observable @CheckReturnValue @SchedulerSupport public open fun combineLatest(p0: Function!, out (???..???)>!, p1: Int, vararg p2: ObservableSource!): Observable<(???..???)>! defined in io.reactivex.Observable
编辑 2
RxKotlin 解决了答案中提到的问题。
import io.reactivex.rxkotlin.Observables
class TestKotlin {
fun test() {
Observables.combineLatest(strings(), booleans(), integers(), this::combine)
}
}
您也可以在 Kotlin 中使用 Function Reference Expression,就像 Java 中的方法引用表达式一样。例如,combine
函数在 Kotlin 中引用为 KFunction3
,如下所示:
val f: kotlin.reflect.KFunction3<String, Boolean, Int, Double> = this::combine
在 java 中,方法引用表达式可以分配给任何 compatible Functional Interface,但不能将 Function Reference Expression 分配给任何 Function Reference Expression函数类型,即使它们 兼容 ,例如:
val f:io.reactivex.functions.Function3<String,Boolean,Int,Double> =this::combine
// type mismatch error ---^
确实,Kotlin 使用 SAM Conversions 将 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中使用Function Reference Expression却失败了?
根据上面,可以看到rx-java2中有很多重载的combineLatest
方法。例如,以下两个无法使 Kotlin 正确使用 Function Reference Expression:
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 Conversions 将 lambda/Function Reference Expression 转换为 Java Functional Interface,但它不能直接分配给 Java Functional Interface。
Kotlin中combineLatest
方法的第4个参数,编译器根本无法推断出它的类型,因为第4个参数类型可以是io.reactivex.functions.Function3
或ObservableSource
。因为 ObservableSource
也是一个 Java Functional Interface.
当你遇到像这样的SAM Conversion冲突时,你可以使用适配器函数将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) }
}
然后你可以使用Function Reference Expression如下,例如:
Observable.combineLatest(
strings(),
booleans(),
integers(),
// v--- convert KFunction3 to RxFunction3 explicitly
this::combine.toFunction3()
)
@holi-java 答案很好,解释了为什么会出现这个问题,这里我为那些只想摆脱这个问题的人提供一个快速修复:
在 rxJava2
中使用 io.reactivex.rxkotlin.Observables
而不是 io.reactivex.Observable
来组合您的观察值:
Observables.combineLatest(src1, src2, ::yourFunction)
这应该开箱即用。