传递lambda而不是接口

Vik*_*kov 56 lambda kotlin

我创建了一个界面:

interface ProgressListener {
    fun transferred(bytesUploaded: Long)
}
Run Code Online (Sandbox Code Playgroud)

但只能将它用作匿名类,而不是lambda

dataManager.createAndSubmitSendIt(title, message,
object : ProgressListener {
    override fun transferred(bytesUploaded: Long) {
        System.out.println(bytesUploaded.toString())
    }
})
Run Code Online (Sandbox Code Playgroud)

我认为应该有可能用lambda替换它:

dataManager.createAndSubmitSendIt(title, message, {System.out.println(it.toString())})
Run Code Online (Sandbox Code Playgroud)

但我收到错误:类型不匹配; required - ProgressListener,found - () - > Unit?

我究竟做错了什么?

mar*_*ran 35

正如@ zsmb13所说,SAM转换仅支持Java接口.

您可以创建一个扩展功能,使其工作:

// Assuming the type of dataManager is DataManager.
fun DataManager.createAndSubmitSendIt(title: String, 
                                      message: String, 
                                      progressListener: (Long) -> Unit) {
    createAndSubmitSendIt(title, message,
        object : ProgressListener {
            override fun transferred(bytesUploaded: Long) {
                progressListener(bytesUploaded)
            }
        })
}
Run Code Online (Sandbox Code Playgroud)

  • “有趣的界面”——几乎没有写过更真实的词语。 (6认同)

zsm*_*b13 28

Kotlin仅支持Java接口的SAM转换.

...请注意,此功能仅适用于Java互操作; 由于Kotlin具有适当的函数类型,因此不需要将函数自动转换为Kotlin接口的实现,因此不受支持.

- 官方文件

如果要在参数中使用lambda,请使函数采用函数参数而不是接口.(至少现在.支持Kotlin接口的SAM转换是一个持续的讨论,它是Kotlin 1.1直播流未来可能的功能之一.)

  • 可能还值得一提的是,如果接口的初衷是表达预期函数的语义,那么你可以使用[typealiases](https://kotlinlang.org/docs/reference/whatsnew11.html#type-别名)或具有命名参数的函数类型,如`(bytesUploaded:Long) - > Unit`. (7认同)

Lou*_*sai 8

派对有点晚了:不要创建一个接口,而是让编译通过直接使用函数而不是数据管理器中的接口来创建一个,如下所示:

fun createAndSubmitSendIt(title: String, message: String, transferred: (Long) -> Unit) {
    val answer = TODO("whatever you need to do")
    transferred(answer)
}
Run Code Online (Sandbox Code Playgroud)

然后你就像你想要的那样使用它!如果我没记错的话,kotlin/jvm编译器的功能与创建接口相同.

希望能帮助到你!


fth*_*dgn 7

Kotlin 1.4 及更高版本

Kotlin 1.4 将通过“功能接口”解决这个问题

Kotlin 函数式接口

  • Kotlin API:完美
  • Kotlin 访问:完美
  • Java访问:完美
class KotlinApi {
    fun interface Listener {
        fun onResponse(response: String)
    }

    fun demo(listener: Listener) {
        listener.onResponse("response")
    }
}

fun kotlinConsumer() {
    KotlinApi().demo { response ->
        println(response)
    }
}

public static void javaConsumer(){
    new KotlinApi().demo(response -> {
        System.out.println(response);
    });
}
Run Code Online (Sandbox Code Playgroud)

在 Kotlin 1.4 之前

如果您的目标是从 Kotlin 和 Java 获得最佳访问体验,则没有针对此问题的单一最终解决方案。

如果 Kotlin 开发人员没有认为 Kotlin 接口的 SAM 转换是不必要的,那么“Kotlin 接口”方法将是最终的解决方案。

https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
另请注意,此功能仅适用于 Java 互操作;由于 Kotlin 具有适当的函数类型,因此无需将函数自动转换为 Kotlin 接口的实现,因此不受支持。

为您的用例选择最佳解决方案。

Kotlin 函数类型

  • Kotlin API:完美
  • Kotlin 访问:完美
  • Java访问:
    • 自动生成的参数类型,如 Function1(对于 Java 8 lambda 不是大问题)
    • 详细return Unit.INSTANCE;而不是 void 返回。
class KotlinApi {
    fun demo(listener: (response: String) -> Unit) {
       listener("response")
    }
}

fun kotlinConsumer() {
    KotlinApi().demo { response->
        println(response)
    }
}

public static void javaConsumer() {
    new KotlinApi().demo(response -> {
        System.out.println(response);
        return Unit.INSTANCE;
    });
}
Run Code Online (Sandbox Code Playgroud)

Kotlin 接口

  • Kotlin API:附加接口定义。
  • Kotlin Access:过于冗长
  • Java访问:完美
class KotlinApi {
    interface Listener {
        fun onResponse(response: String)
    }

    fun demo(listener: Listener) {
       listener.onResponse("response")
    }
}

fun kotlinConsumer() {
    KotlinApi().demo(object : KotlinApi.Listener {
        override fun onResponse(response: String) {
            println(response)
        }
    })
}

public static void javaConsumer() {
    new KotlinApi().demo(response -> {
        System.out.println(response);
    });
}
Run Code Online (Sandbox Code Playgroud)

Java接口

  • Kotlin API:混合 Java 代码。
  • Kotlin Access:有点冗长
  • Java访问:完美
class KotlinApi {
    fun demo(listener: Listener) {
        listener.onResponse("response")
    }
}

public interface Listener {
    void onResponse(String response);
}

fun kotlinConsumer() {
    KotlinApi().demo { response ->
        println(response)
    }
}

public static void javaConsumer() {
    new KotlinApi().demo(response -> {
        System.out.println(response);
    });
}
Run Code Online (Sandbox Code Playgroud)

多种方法

  • Kotlin API:多种方法实现
  • Kotlin Access:如果使用正确的方法,那就完美了。自动完成也建议详细方法。
  • Java访问:完美。由于JvmSynthetic注释,自动完成不建议函数类型方法
class KotlinApi {
    interface Listener {
        fun onResponse(response: String)
    }

    fun demo(listener: Listener) {
        demo { response ->
            listener.onResponse(response)
        }
    }

    @JvmSynthetic //Prevents JVM to use this method
    fun demo(listener: (String) -> Unit) {
        listener("response")
    }
}

fun kotlinConsumer() {
    KotlinApi().demo { response ->
        println(response)
    }
}

public static void javaConsumer() {
    new KotlinApi().demo(response -> {
        System.out.println(response);
    });
}
Run Code Online (Sandbox Code Playgroud)

Java API

  • Kotlin API:没有 Kotlin API,所有 API 代码都是 Java
  • Kotlin 访问:完美
  • Java访问:完美
public class JavaApi {
    public void demo(Listener listener) {
        listener.onResponse("response");
    }

    public interface Listener {
        void onResponse(String response);
    }

}

fun kotlinConsumer() {
    JavaApi().demo { response ->
        println(response)
    }
}

public static void javaConsumer() {
    new JavaApi().demo(response -> {
        System.out.println(response);
    });
}
Run Code Online (Sandbox Code Playgroud)


ces*_*rds 5

另一个解决方案是声明一个类型别名,将其注入某个地方并调用它。这里的例子:

internal typealias WhateverListener = (String) -> Unit
Run Code Online (Sandbox Code Playgroud)

然后将这种类型别名注入到我们的类中:

class Gallery constructor(private val whateverListener: WhateverListener) {

    ...

    galleryItemClickListener.invoke("hello")

    ...
}
Run Code Online (Sandbox Code Playgroud)

所以我们有lambda:

val gallery = Gallery { appNavigator.openVideoPlayer(it) }
Run Code Online (Sandbox Code Playgroud)

归功于我的同事乔尔·佩德拉萨(Joel Pedraza),他在尝试找到解决方案<3时向我展示了诀窍。