委托:有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
在Kotlin中,对委托
进行了简化,通过by
就可以实现委托的效果。例如前面提到的by lazy
延迟初始化就是利用了委托
。
Kotlin中主要分为两种:类委托、委托属性 。
基本语法:
1 val /var <属性名>:<类型> by <表达式>
类委托
在不使用继承的情况下,拓展一个类的功能,使之提供更多的功能。类似装饰模式
。
装饰模式
的缺点就是需要较多的样板代码,装饰类需要实现接口的全部方法,并需要调用到原始对象的方法。
Kotlin可以零样板代码进行原生支持 ,通过by
关键字进行实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 interface Base { fun print () }class BaseImpl (val x: Int ) : Base { override fun print () { print(x) } }class Derived (b: Base) : Base by bfun main (args: Array <String >) { val b = BaseImpl(10 ) Derived(b).print() }
利用by
,将新类的接口委托给原始对象,生成后的新类会自动生成接口方法,并默认返回原始类的具体实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public final class Derived implements Base { private final Base $$delegate_0; public Derived (@NotNull Base b) { Intrinsics.checkParameterIsNotNull(b, "b" ); super (); this .$$delegate_0 = b; } public void print () { this .$$delegate_0.print(); } }
观察上述反编译后的Java代码,看到实际生成的Derived
已经默认实现了接口方法。可以按照需求去重载某些方法,而不必写过多的样板代码。
委托属性
将属性的访问器(get()、set()
)委托给一个符合属性委托约定规则的对象。
委托属性对象规则:
ReadOnlyProperty
由Kotlin提供的接口,方便开发者使用于val
声明变量
ReadWriteProperty
由Kotlin提供的接口,方便开发者使用于var
声明变量
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Person { var name:String by MyDelegate() }class MyDelegate : ReadWriteProperty <Any?,String > { override fun getValue (thisRef: Any ?, property: KProperty <*>) : String { return "Kotlin" } override fun setValue (thisRef: Any ?, property: KProperty <*>, value: String ) { } }
Kotlin自带委托 延迟初始化
利用变量 by lazy
可以实现 延迟初始化。
使用示例 1 2 3 4 5 class Demo (){ val sex: String by lazy { "male" } }
lazy
接收初始化该值的lambda
表达式,并返回一个getValue()
的对象。
原理分析 先分析反编译该段代码后的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public final class Demo { @NotNull //生成对应参数的委托信息 private final Lazy sex$delegate; static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property( new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(Bird.class), "sex", "getSex()Ljava/lang/String;"))}; @NotNull public final String getSex() { Lazy var1 = this.sex$delegate; KProperty var3 = $$delegatedProperties[0]; boolean var4 = false; //在第一次使用的时候 获取对应数据 return (String)var1.getValue(); } public Demo(){ //执行指定的初始代码块 this.sex$delegate = LazyKt.lazy((Function0)null.INSTANCE); } }
最终通过获取lazy
函数的getValue()
获取所需结果。
属性改变通知
通过调用value by Delegates.observable(value)
来监听value
的数据变化。
另外还提供了value by Delegates.vetoable(value)
也同样起到监听的效果,但是该方法返回一个Boolean
类型来判断是否需要对value进行赋值。
使用示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var age: Int by Delegates.observable(age) { property, oldValue, newValue -> println("${property.name} oldValue=>$oldValue newValue=>$newValue " ) } age = 3 age = 4 println(age) var age: Int by Delegates.vetoable(age) { property, oldValue, newValue -> println("${property.name} oldValue=>$oldValue newValue=>$newValue " ) true } age = 3 age = 4 println(age)
原理分析 先分析Delegates
相关代码的实现
1 2 3 4 5 6 7 8 9 10 public inline fun <T> observable (initialValue: T , crossinline onChange: (property : KProperty <*>, oldValue : T , newValue : T ) -> Unit ) : ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) { override fun afterChange (property: KProperty <*>, oldValue: T , newValue: T ) = onChange(property, oldValue, newValue) }public inline fun <T> vetoable (initialValue: T , crossinline onChange: (property : KProperty <*>, oldValue : T , newValue : T ) -> Boolean ) : ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) { override fun beforeChange (property: KProperty <*>, oldValue: T , newValue: T ) : Boolean = onChange(property, oldValue, newValue) }
observable
通过ObservableProperty.afterChange()
来监听变化,vetoable
通过ObservableProperty.beforeChange()
来监听变化并对数据的赋值进行拦截
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public abstract class ObservableProperty <T > (initialValue: T) : ReadWriteProperty<Any?, T> { private var value = initialValue protected open fun beforeChange (property: KProperty <*>, oldValue: T , newValue: T ) : Boolean = true protected open fun afterChange (property: KProperty <*>, oldValue: T , newValue: T ) : Unit {} public override fun getValue (thisRef: Any ?, property: KProperty <*>) : T { return value } public override fun setValue (thisRef: Any ?, property: KProperty <*>, value: T ) { val oldValue = this .value if (!beforeChange(property, oldValue, value)) { return } this .value = value afterChange(property, oldValue, value) } }
可变属性延迟初始化
by lazy
只对val变量
可用,当变量为var
时则无法使用,这时就需要用到var value by Delegates.notNull<String>
来表示
使用示例 1 2 3 4 5 6 7 8 class Demo { var value by Delegates.notNull<String> init { a= "init" } }
原理分析
自定义委托