Kotlin 避坑:小心数据类中定义的 Lambda 成员

前言

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 做了特别对待,将其视为稳定类型,所以没关系。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fundroid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值