by 关键字

347 阅读1分钟

关键字 by 在 kotlin 主要用于委托实现Kotlin 编译器会额外生成一个类用来处理委托,这就是委托实现的底层逻辑。

类委托

  1. 如果被委托类实现多个接口,只有每一个接口都需要使用 by 关键字定义委托实现
  2. 如果不同的接口有相同方法,当前类必须自己实现该方法
  3. 被委托类未实现的方法直接会代理给委托类,已实现的方法不会变化。
  4. 其原理是:kotlin 在编译生成 class 文件时会自动将未实现的方法代理给委托类中同一方法

如下:

// 多接口委托
class Test: Itf1 by Itf1Impl(),Itf2 by Itf2Impl(){
    override fun itf() {
        
    }
}
// 单接口委托
class Test(itf:Itf):Itf2,Itf by itf{
    override fun test2() { // 定义在 Itf 接口中
        // test() 方法 Test 类自己实现了,不需要委托给别的对象
        Log.e(TAG, "test: self")
    }
}

通过反编译相应的 class 文件,可发现 构造函数中使用成员变量记录委托类,并在相应的方法中调用委托类对应的方法。首先是构造函数

image.png

下面是 test() 方法,整体没任何多余逻辑,就是将请求转给 $$delegate_0 字段

image.png

属性委托

  1. 将一个属性的 getter/setter 委托给另一个类的 getValue/setValue 方法。
  2. 其原理是:使用成员变量记录委托类,在生成的 getter/setter 中自动调用 getValue/setValue
  3. 当前类不会存储属性值,因此委托类可以根据需要决定存储或不存储属性值。可以用来实现对属性值的过滤

如下,使用 i 属性时就会调用到下面的 getValue/setValue

class Test{
    // 将 i 委托给 IDelegate 对象
    // 每一次对 i 的 getter/setter 都会调用 IDelegate 的 getValue 与 setValue 方法
    var i: Int by IDelegate() 
}

// 新版本的 studio 会自动生成 getValue/setValue 方法,不需要记具体写法
class IDelegate{
    // delegate 参数必须是 Test 的父类
    operator fun getValue(delegate: Any, property: KProperty<*>): Int {
        return 1
    }

    operator fun setValue(delegate: Any, property: KProperty<*>, i: Int) {

    }
}

同样,反编译对应的 class 文件可以看出:

  1. 构造函数中会生成一个 IDelegate 类型的成员变量, 名为 i$delegate
  2. igetter/setter 中会调用 i$delegategetValue/setValue 方法

image.png

以生成的 setter 为例

image.png

lazy

其原理是懒汉式单例,构造函数中首先会将 lambda 表达式封装在一个 Lazy 对象中。在属性的 getter 方法中会调用 Lazy 对象的 getValue 方法,getValue 会使用懒汉式写法保存属性值

可观察属性 Delegates.observable

当为属性赋值时,会回调第二个参数 lambda 表达式

lambda 表达式会被转换成一个类,该类继承自 ObservableProperty(它又实现了 ReadWriteProperty 接口),同时将 lambda 表达式的内容填充到 afterChange 方法中

属性的 getter/setter 都被代理给 ReadWritePropertygetValue/setValue。所以当为属性赋值时就是调用 ObservablePropertygetValue 方法,该方法又会调用 afterChange 方法,也就是我们的 lambda 表达式。


private var i: Int by Delegates.observable(-1){_,o,n->
    Log.e(TAG, "o = : $o, n = $n" )
}

Delegates.vetoable

与上面的 observable 一样,只不过 lambda 表达式会被封装到 beforeChange 中,可以检测赋值给属性的值是否合理。如果返回为 true 该值就是合理的,否则不合理。

具体逻辑在 ReadWritePropertysetValue 方法中。