【问题标题】:How to solve the lambda / SAM problem in Kotlin multiplatform library projects?如何解决 Kotlin 多平台库项目中的 lambda / SAM 问题?
【发布时间】:2019-07-11 09:34:16
【问题描述】:

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

这种方法有很多样板,也很难维护。有没有更好的办法来解决这个问题?

【问题讨论】:

  • 为什么不两者兼而有之?让它超载。
  • 我两者都用,但它带有很多的样板文件。我正在寻找一种解决方案,它可以让我编写更少且可维护的代码。
  • 所以问题是函数调用的SAM转换只发生在从kotlin调用java而不是从kotlin调用kotlin时......为什么这么愚蠢。是否可以向 kotlin 团队请求此功能?

标签: java kotlin kotlin-multiplatform


【解决方案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

【讨论】:

    【解决方案2】:

    我刚刚添加了一些便利函数,让我可以轻松地将 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&lt;T, R&gt; 的歧义。您也可以将$ 用于消费者,但是您需要大括号或者必须将其转换为Consumer&lt;T&gt;-interace 以利用方法引用,例如:

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

    使用带有反引号的 $ 的主要好处是,在 Kotlin 中您可能不会使用它,因为它不是那么容易写(谁在调用函数时以反引号开头?)和代码完成并不那么容易。

    此外,$-sign 可以在 Java 端直接调用,没有问题。

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

    【讨论】:

    • 如果我是用户,我真的不喜欢这样的东西。重点是为 Java 和 Kotlin 用户提供 惯用 界面。
    • 嗯...我还想宣传一下 Kotlin 的使用 ;-) 因为它很好.. 也许你想生成这些重载?
    • hmmm... 如果新类型推断是默认值,那么使用 Java 函数类型的代码将与使用 Kotlin 函数类型一样可用,对吧?您是否已经尝试过新的类型推断?
    • 请注意,我的问题仅存在于多平台环境中。我无权访问任何 Java 代码。
    • 好吧..在您的问题中,您将其范围缩小到 Java/Kotlin 互操作... AFAIK,新的类型推断可以帮助您...想要使用自己的函数类型以及actual/expect 这样,也许每个平台只能看到其可能的变体......但是还没有时间,可能不会超过 3 周......
    猜你喜欢
    • 1970-01-01
    • 2020-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    相关资源
    最近更新 更多