大家都知道,Kotlin有高阶函数,每个函数的函数类型由函数的形参列表、->和返回值类型组成。比如:
fun pow(base : Int, expo : Int) : String {}
// 使用::将函数名称赋值给变量
var myfun : (Int, Int)->String = ::pow
函数类型就像数据类型一样,既可用于定义变量,也可用作函数形参类型,还可作为函数的返回值类型,像C的函数指针。
最近在项目开发中遇到一个坑,简要的代码是这样的:
object A {
data class Job(private val foo: WeakReference<(Int)->Unit>)
private val queue = Queue<Job>()
fun doWork() {
// ...
job = queue.get(0)
job.get()?.invoke(1)
}
fun addJob(job: Job) {
queue.add(job)
}
}
class B() {
fun add(i : Int) {
}
fun foo() {
A.addJob(Job(WeakReference(::add)))
}
}
A和B都在一个前台Activity C里,某个时刻会触发A的doWork方法,回调到B的add方法,这个就是背景。开发过程中发现job.get()拿出来的一直是个空,按理来说C拿着B,add函数又是在B里面,这个引用链应该是没问题的。后来把它编译成字节码后看了下,变成了类似下面这样:
class A {
class Job{
private final WeakReference<Function1<Int, Unit>> callback;
}
//...
}
class B {
public void foo() {
A.addJob(Job(WeakReference(new D(this))))
}
}
class D{
private B b;
D(B b) {
this.b = b
}
public void foo1(int i){
b.add(i)
}
}
传给WeakReference的竟然变成一个匿名对象,这个没人引用这个匿名对象,肯定马上就被回收了。
后来把B改成下面这样就好了:
class B() {
private val add = { i: Int ->
//...
}
fun foo() {
A.addJob(Job(WeakReference(add)))
}
}
看编译后的,这个时候传给WeakReference的就是一个成员变量了。
对新的语法还是不够了解,才会遇到这个坑。。。