Kotlin的SAM转换和踩坑
只有一个抽象方法的接口被称作函数式接口,或者单抽象方法(SAM)接口。函数式接口应用广泛,我们常见的Runnable、View.OnClickListener等都是函数式接口。
An interface with only one abstract member function is called a functional interface, or a Single Abstract Method (SAM) interface. ——kotlinlang.org
只有一个抽象方法的接口被称作函数式接口,或者单抽象方法(SAM)接口。函数式接口应用广泛,我们常见的Runnable、View.OnClickListener等都是函数式接口。
SAM转换
正常情况下,我们实现一个接口是比较麻烦的,如下
private val redRunnable = object: Runnable {
override fun run() {
binding.main.setBackgroundColor(Color.RED)
}
}
Kotlin为我们提供了语法糖,当接口是一个SAM接口时,AS会提示我们方法可以简化:
private val redRunnable = Runnable { binding.main.setBackgroundColor(Color.RED) }
SAM转换的踩坑
上述的例子我们做一个小Demo,两个按钮控制背景色,第一个按钮延迟2s设置红色背景,第二个按钮立即设置绿色背景。很容易写出以下的代码:
private val redRunnable = { binding.main.setBackgroundColor(Color.RED) }
private fun bindView() {
binding.button1.setOnClickListener {
handler.postDelayed(redRunnable, 2_000)
}
binding.button2.setOnClickListener {
handler.removeCallbacks(redRunnable)
binding.main.setBackgroundColor(Color.GREEN)
}
}
依次点击按钮1和按钮2,发现背景先变为绿色再变为红色,显然按钮2并没有成功取消掉按钮1的delay操作。你知道以上代码哪里出问题了吗?
AI的回复大略一看没有解决问题,这个任务显然没有执行,也没有生命周期问题。于是贴上完整代码,AI立即解决了问题。我遇到这个问题时没有贴代码给AI,苦思冥想许久才看出端倪:我这里定义的redRunnable并不是一个真正的Runnable对象,而是一个lambda(类型()->Unit)。这就导致postDelayed和removeCallbacks时SAM转换生成了不同的Runnable对象,因此取消就无法成功。
接下来看一下反编译出的java代码来验证这一猜想:
最后把这段代码修改正确,
最后修改于 2024-09-24