Kotlin类委托和属性委托
kotlin类委托
使用kotlin类委托
kotlin的类委托主要是接口代理,类似Java中代理模式。委托机制:类A需要实现的操作委托给类B来实现。举个例子:假设有个人A需要打官司但是不会,于是委托给律师B来打官司,在这个过程中A委托了B,B代理A来打官司,委托代理成对出现,一般先有委托再有代理,这个过程也可以通过代码来实现:
1 | /** |
通过kotlin字节码反编译,可以很明显的看出其实现方式就是通过Java的代理模式来实现的:
1 | // 打官司接口 |
覆写委托类方法
有些时候在委托类实现的情况下,又需要自定义方法实现,在这种情况下可以通过覆写委托类方法来实现,具体如以下代码:
1 | class ExampleList<T> (val innerList : MutableCollection<T> = mutableListOf()) : MutableCollection<T> by innerList{ |
kotlin属性委托
定义一个被委托的类
与类委托类似,属性委托也是通过by关键字来定义的,其格式为:var/val 变量 :Type by 委托类,具体代码如下:
1 | /** |
注意:
对于只读属性,即val定义的变量,它的委托类必须提供一个getValue函数,且函数用operator关键字修饰,getValue有两个参数:
thisRef:根据以上运行结果,thisRef指的是当前调用对象(即属性所有者),其类型约束与调用对象一致或者是调用对象的超类型(子类型)property:当前属性所持有者的反射,其类型约束为KProperty<*>或者是KProperty<*>的超类型(子类型)
对于可读写属性,即var定义的变量,它的委托类除了提供getValue函数,还需要提供一个setValue函数,同样的,setValue函数也需要用operator关键字来修饰,setValue函数接收3个参数:
thisRef:同getValue()property:同getValue()new value:提供给当前属性用来赋新值的参数,其类型与当前属性一致或其超类型(子类型)
通过字节码将其转为Java代码,查看属性委托其原理:
1 | public final class ProxyExample { |
标准委托
在kotlin标准库中也内置了很多工厂方法实现属性的委托。
延迟加载–Lazy()函数
延迟加载lazy()函数的用法,具体如下代码:
1 | class ExampleByLazy{ |
val example by lazy中lazy是标准库中的一个函数,该函数传入的参数是一个lambda表达式,其返回值是lazy<T>,即属性example实际上是委托给了lazy<T>类
1 | public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer) |
lazy函数返回的是lazy<T>泛型类,但是实际上返回的是SynchronizedLazyImpl<out T>
1 | private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable { |
1 | .internal.InlineOnly |
泛型类lazy<T>实现了一个getValue 的扩展方法,这是被委托类必须要实现的一个方法,getValue函数返回的是lazy<T>中的value属性值,SynchronizedLazyImpl<out T>是lazy<T>的具体实现类,因此实际返回的是SynchronizedLazyImpl<out T>中的value属性值,value属性值最终返回的就是lambda表达式返回的值
注意:
- 泛型类
lazy<T>并没有实现setValue的扩展函数,所以委托给lazy<T>的属性只能够使用val来声明而不能够使用var声明 lazy函数是懒加载函数,只有第一次调用时能够完整执行lambda函数,之后调用都只是返回值
1 | class ExampleByLazy{ |
可观察属性Observable
顾名思义,Dalegates.obserable()就是可观察的属性,可以对属性变化进行观察和处理,Delegates.observable传入两个参数:
- initialValue:提供该属性的初始值
- onChange:提供一个含有三个参数的lambda表达式,用于观察属性变化后对属性变化的额外处理,三个参数分别是
property: KProperty<*>, oldValue: T, newValue: T,分别代表被赋值的属性,旧值,新值
Delegates.observable使用:
1 | // Delegates.observable |
跟踪Delegates.observable代码实现,理解其具体实现过程:
1 | public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit): |
1 | public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> { |
observable是单例类Delegates中的一个方法,observable方法返回了类ObservableProperty,ObservableProperty是一个抽象类,所以需要覆写afterChange函数,afterChange函数实际上就是传递进来的onChangelambda表达式,因而demo中ExampleObservable中value属性实际上是被委托给了ObservableProperty类,这样整个实现就一目了然了
集合map
kotlin为Map实现了getValue扩展函数,所以Map可以作为属性委托类,使用如下:
1 | class ExampleMap(val map: Map<String,Any?>){ |
Delegates.NotNull
与Delegates.observable用法类似,不过Delegates,NotNull适用于那些无法在初始化阶段就确定属性值的场合。
1 | class ExampleNotNull(){ |
注意:使用Delegates.NotNull如果属性在赋值前访问会抛出异常
1 | Exception in thread "main" java.lang.IllegalStateException: Property notNullValue should be initialized before get. |