【问题标题】:Kotlin setOnClickListener syntax using lambda使用 lambda 的 Kotlin setOnClickListener 语法
【发布时间】:2021-03-17 23:08:36
【问题描述】:

在 Kotlin 中设置点击监听器时,我们可以这样写:

rollButton.setOnClickListener(
    object: View.OnClickListener {
        override fun onClick(v: View?) {
            rollDice();
        }
    }
)

SAM转换后,我们可以写成:

rollButton.setOnClickListener({ v -> rollDice() })

但我注意到 kotlin 也接受:

rollButton.setOnClickListener(View.OnClickListener() { v -> rollDice() })

还有

rollButton.setOnClickListener(View.OnClickListener { v -> rollDice() })

我想知道这里发生了什么?我的意思是 setOnClickListener 应该使用匿名类或 SAM 速记之类的东西来实现 OnClickListener。这是某种中间阶段还是什么?

非常感谢任何解释或资源链接。

【问题讨论】:

  • “Kotlin 可以将任何签名与接口的单个​​方法的签名匹配的 lambda 表达式转换为实现该接口的类的实例” (source) 和 “如果 [a] lambda 是 [a function] 的唯一参数,则括号可以省略” (source)。
  • 因此你可以写rollButton.setOnClickListener({ v -> rollDice() })甚至rollButton.setOnClickListener { v -> rollDice() }之类的东西
  • 现在我了解了 SAM 转换实际上是什么,rollButton.setOnClickListener({ v -> rollDice() }) 是如何工作的?接口名称不见了!
  • 编译器知道setOnClickListener 需要OnClickListener

标签: android kotlin lambda


【解决方案1】:

对于这些示例,让接口为:

fun interface IntUser {
    fun use(value: Int)
}

SAM 转换让您可以像构造函数一样使用接口的名称,将 lambda 作为参数。

val x = IntUser({ println(it) })

Trailing lambda syntax 允许您将 lambda 移动到括号外,就像您的第二个示例一样:

val y = IntUser() { println(it) }

当 lambda 是唯一参数时使用尾随 lambda 语法时,您可以省略括号,就像在第三个示例中一样:

val z = IntUser { println(it) }

最后,SAM 转换可以做的另一件事是让您传递一个裸露的 lambda 来代替接口参数。它可以从函数参数中推断出类型:

rollButton.setOnClickListener {
    rollDice()
}

这里我们使用 lambda 作为唯一参数,因此我们还使用尾随 lambda 语法并省略空括号。

【讨论】:

  • 很好的解释,可能只是忽略了这仅适用于功能接口。
  • @m0skit0,SAM 或单一抽象方法接口也称为功能接口,不是吗?
  • Java 接口确实如此,但对于 Kotlin 接口,您还必须将 fun 放在 interface 之前,才能将其视为函数式接口并允许 lambda 转换。
  • 是的,我的意思不是“让你传递一个裸露的 lambda 代替接口参数”,而是说“让你传递一个裸露的 lambda 代替功能接口”更正确参数”等。
  • 我在回复 OP。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多