如何解决Kotlin多平台库项目中的lambda / SAM问题?

时间:2019-07-11 09:34:16

标签: java kotlin kotlin-multiplatform

我对Kotlin中的SAM有问题。我正在使用具有接受lambda函数的库。我的问题是我不能简单地这样写:

fun myFun(someLambda: (A) -> B) {
    // ...
}

因为如果Java用户想要调用它,则需要将Function1传递给它,这不是一个很好的UX。如果我改为创建SAM:

fun myFun(someSam: Function<A, B>) {
    // ...
}

然后,这对于Kotlin用户来说很麻烦。到目前为止,我一直在做SAM的工作,为Kotlin用户添加了所有这些功能的扩展功能,将其转换为SAM功能:

inline fun <A, B> MyClass.someFun(crossinline fn: (A) -> B) {
    return someFun(object : Function<A, B> {
        override fun accept(value: A): B {
            return fn.invoke(value)
        }
    })
}

此方法附带许多样板,并且也很难维护。有解决这个问题的更好的选择吗?

2 个答案:

答案 0 :(得分:1)

在 kotlin 1.4 中,您现在可以像这样声明函数式 (SAM) 接口:

fun interface MyFunction {
    fun doSomething(input: Int): Boolean
}

注意接口声明前面的“fun”关键字。这将允许使用来自 kotlin 和 java 的 lambda。

另见https://kotlinlang.org/docs/reference/fun-interfaces.html

答案 1 :(得分:0)

我刚刚添加了一些便利功能,这些功能使我可以轻松地将Java功能接口转换为Kotlin对应的功能,但是却把它们放回了对号(我不喜欢在常规Kotlin代码中看到(与测试相反)); -)),例如:

fun <T> `$consume`(consumer: Consumer<T>): (T) -> Unit = consumer::accept
fun <T, R> `$`(func: java.util.function.Function<T, R>): (T) -> R = func::apply
fun <T, U, R> `$`(func: java.util.function.BiFunction<T, U, R>): (T, U) -> R = func::apply
// etc.

假设您在Kotlin中具有以下功能:

fun doSomething1(c : (String) -> Unit) : String = TODO()
fun doSomething2(f : (String) -> String) : String = TODO()
fun doSomething3(f : (String, String) -> String) : String = TODO()

Java的用法如下:

doSomething1($consume((e) -> System.out.println(e)));
doSomething2($((e) -> e + "ok"));
doSomething3($((e1, e2) -> String.join(", ", e1, e2)));

请注意,我使用$consume来克服关于Function<T, R>的歧义。您也可以为使用者使用$,但是您可能需要大括号,或者必须将其强制转换为Consumer<T>-interace才能利用方法引用,例如:

doSomething1($((e) -> { System.out.println(e); }));
doSomething1($((Consumer<String>) System.out::println));

$与反引号一起使用的主要好处是,在Kotlin中您可能不会使用它,因为它不太容易写(谁在调用函数时以反引号开头?)代码完成并不能很容易地表明这一点。

此外,可以在Java端直接调用$符号,而不会出现问题。

也许您想将重载函数与此混合使用,这样就不需要实现所有重载代码本身并将这些函数放在自己的库中供Java开发人员使用?