前言
Kotlin 中 lambda 的使用非常普遍,但是稍有不慎容易踩坑。本文展示一个 labmda 在 data class 中容易踩的坑,由于两个 lambda 会被当成不同对象,破坏了 data class 的相等性,可能就会引出隐晦的 bug 。
问题
让我们定义个简单的数据类,构造函数里包含 lambda :
data class Action(
val label: String,
val onClick: () -> Unit
)
fun main() {
val action1 = Action("Retry") { println("Retrying…") }
val action2 = Action("Retry") { println("Retrying…") }
println(action1 == action2)
}
输出:
false
虽说这两个 Action 实例的 label 相同,lambda 体也一样,但它们并不相等。
这是因为 Kotlin 里的 lambda 是按引用比较的。每个 { println("Retrying...") } 都会在内存里创建新对象,哪怕逻辑一样也是如此。
如何解决
我们把 lambda 提取出来,把同一个引用传给两个实例
val retryLambda: () -> Unit = { println("Retrying…") }
val action1 = Action("Retry", retryLambda)
val action2 = Action("Retry", retryLambda)
println(action1 == action2)
输出:
true
这种情况下,两个实例拿到的是同一个 lambda 引用,所以 equals() 方法比较的是同一个 lambda 对象,结果就是 true 。
所以相等性是基于 lambda 引用的,而非其函数体实现 。
讲完了
本文就这一个知识点,非常短,但是值得提醒。因为这可能破坏 equals() 和 hashCode() 逻辑,可能在一些缓存逻辑中引入 bug,而且在面试题中出现类似问题,你也能轻松应对。
最后,抛一个问题: Compose 中大量使用了尾 lambda 参数作为 DSL,重组时如果比较 Labmda 参数,岂不是永远有 diff,无法跳过重组?
如果你也有此疑问,说明你对 Compose 非常熟悉。答案一言以蔽之:因为 Compose 对 Labmda 做了特别对待,将其视为稳定类型,所以没关系。

1万+

被折叠的 条评论
为什么被折叠?



