【问题标题】:Passing lambda instead of interface传递 lambda 而不是接口
【发布时间】:2017-09-14 02:52:17
【问题描述】:

我已经创建了一个界面:

interface ProgressListener {
    fun transferred(bytesUploaded: Long)
}

但只能将其用作匿名类,不能用作 lambda

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

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

dataManager.createAndSubmitSendIt(title, message, {System.out.println(it.toString())})

但我收到错误:Type mismatch; required - ProgressListener, found - () -> Unit?

我做错了什么?

【问题讨论】:

    标签: lambda kotlin


    【解决方案1】:

    正如@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)
                }
            })
    }
    

    编辑:

    Kotlin 1.4 将带来函数接口,支持对 Kotlin 中定义的接口进行 SAM 转换。这意味着如果您使用 fun 关键字定义接口,则可以使用 lambda 调用您的函数。像这样:

    fun interface ProgressListener {
        fun transferred(bytesUploaded: Long)
    }
    

    【讨论】:

    • fun interface - 几乎没有人写过更真实的文字。
    • yes fun interface 这将使普​​通界面变为功能界面。
    【解决方案2】:

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

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

    -- Official documentation

    如果您想在参数中使用 lambda,请让您的函数采用函数参数而不是接口。 (至少目前是这样。支持 Kotlin 接口的 SAM 转换是一个持续的讨论,它是 Kotlin 1.1 直播中可能的未来功能之一。)

    【讨论】:

    • 可能还值得一提的是,如果接口的初衷是表达预期函数的语义,那么你可以使用typealiases或带有命名参数的函数类型,如(bytesUploaded: Long) -> Unit
    【解决方案3】:

    更新日期:2020 年 9 月 7 日

    正如 Kotlin 1.4 版本的 Kotlin documentation 指出的那样:

    在 Kotlin 1.4.0 之前,您只能在使用来自 Kotlin 的 Java 方法和 Java 接口时应用 SAM(单一抽象方法)转换。从现在开始,您也可以对 Kotlin 接口使用 SAM 转换。为此,请使用 fun 修饰​​符将 Kotlin 接口显式标记为函数式。

    fun interface Operation1 {
        operator fun invoke(x: String): String
    }
    
    fun interface Operation2 {
        fun doSomething(x: Int): String
    }
    
    val operation1 = Operation1 { "$it world!" }
    val operation2 = Operation2 { "$it world!" }
    
    fun main() {
        // Usage: First sample.
        println(operation1("Hello"))
        println(operation2.doSomething(0))
        // Usage: Second sample.
        println(Operation1 { "$it world!" }("Hello"))
        println(Operation2 { "$it!" }.doSomething(0))
    }
    

    您可以阅读更多关于功能接口的信息here

    以前的解决方案:

    声明一个类型别名,将其注入某处并稍后调用它:

    internal typealias WhateverListener = (String) -> Unit
    

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

    class Gallery constructor(private val whateverListener: WhateverListener) {
        
        ...
        
        galleryItemClickListener.invoke("hello")
    
        ...
    }
    

    所以我们有我们的 lambda:

    val gallery = Gallery { appNavigator.openVideoPlayer(it) }
    

    感谢我的同事 Joel Pedraza,他在尝试找到解决方案时向我展示了诀窍

    注意:如果您想知道何时使用函数式接口 (lambda) 或类型别名,请查看 Kotlin documentation

    【讨论】:

    • 这是一个不错的解决方案,您甚至不需要额外的typealias
    • 不支持嵌套和本地类型别名
    【解决方案4】:

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

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

    然后你就可以按照你想要的方式使用它了!如果我没记错的话,kotlin/jvm 编译器所做的与制作接口相同。

    希望对你有帮助!

    【讨论】:

      【解决方案5】:

      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);
          });
      }
      

      在 Kotlin 1.4 之前

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

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

      https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
      另请注意,此功能仅适用于 Java 互操作;自从科特林 具有适当的函数类型,自动将函数转换为 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;
          });
      }
      

      Kotlin 接口

      • Kotlin API:附加接口定义。
      • Kotlin 访问:过于冗长
      • 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);
          });
      }
      

      Java 接口

      • Kotlin API:混合 Java 代码。
      • Kotlin 访问:有点冗长
      • 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);
          });
      }
      

      多种方法

      • Kotlin API:多种方法实现
      • Kotlin 访问:如果使用正确的方法,那就完美了。自动完成也建议使用详细方法。
      • 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);
          });
      }
      

      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);
          });
      }
      

      【讨论】:

      • 很好的答案!感谢您提供 Java 示例。当高阶函数返回 Unit 时,我遇到了问题应该返回什么。
      猜你喜欢
      • 2011-10-26
      • 2023-03-15
      • 2022-01-06
      • 2019-06-21
      • 1970-01-01
      • 2019-06-29
      • 2015-02-16
      • 2019-01-03
      • 1970-01-01
      相关资源
      最近更新 更多