属性代理是Kotlin独有的功能。
1. 简介 属性代理是借助Kotlin中代理设计模式,把这个模式应用于一个属性时,它可以将属性访问器的逻辑代理给一个对象。即,将setter和getter的实现,都交给一个对象来实现,这个对象需要实现getValue和setValue方法,既可以是普通方法,也可以是扩展方法。使用属性时,依然照常使用,只是访问器会调用扶助对象的setValue和getValue。
1 2 3 4 5 6 7 8 class Foo { var name: String by NameDelegate() } class NameDelegate { operator fun <T> getValue (thisRef: Any ?, property: KProperty <*>) : T {} operator fun <T> setValue (thisRef: Any ?, property: KPproperty <*>, value: T ) {} }
上面便是属性代理的定义形式。另外,Kotlin还提供了几个常见的属性代理
1 2 3 4 Delegates.notNull() Delegates.observable() Delegates.vetoable()
2. 几个结构 在介绍Kotlin提供的代理之前,先要说2个它的接口,很好理解
ReadOnlyProperty,只读属性,只有getValue方法1 2 3 public fun interface ReadOnlyProperty<in T, out V> { public operator fun getValue (thisRef: T , property: KProperty <*>) : V }
ReadWriteProperty,可读写属性,既有getValue方法,也有setValue方法1 2 3 4 public interface ReadWriteProperty <in T, V > : ReadOnlyProperty <T, V > { public override operator fun getValue (thisRef: T , property: KProperty <*>) : V public operator fun setValue (thisRef: T , property: KProperty <*>, value: V ) }
3. Delegates.notNull() notNull是一个方法,返回的就是一个带有getValue和setValue方法的对象,其中主要代码如下
1 2 3 4 5 6 7 8 9 10 11 private class NotNullVar <T : Any > () : ReadWriteProperty<Any?, T> { private var value: T? = null public override fun getValue (thisRef: Any ?, property: KProperty <*>) : T { return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get." ) } public override fun setValue (thisRef: Any ?, property: KProperty <*>, value: T ) { this .value = value } }
可以看到,NotNullVal实现了可读写属性的接口,setValue没有变,只是赋值给成员变量,而getValue加了空判断。在非空问题上,Kotlin还提供了一个lateinit关键字,但是这个关键字只能用来修饰引用类型的变量,而notNull适用于基础数据类型和引用类型。
4. Delegates.observable() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public abstract class ObservableProperty <V > (initialValue: V) : ReadWriteProperty<Any?, V> { private var value = initialValue protected open fun beforeChange (property: KProperty <*>, oldValue: V , newValue: V ) : Boolean = true protected open fun afterChange (property: KProperty <*>, oldValue: V , newValue: V ) : Unit {} public override fun getValue (thisRef: Any ?, property: KProperty <*>) : V { return value } public override fun setValue (thisRef: Any ?, property: KProperty <*>, value: V ) { val oldValue = this .value if (!beforeChange(property, oldValue, value)) { return } this .value = value afterChange(property, oldValue, value) } } 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) }
ObservableProperty,也是实现了可读写的接口,相比于可读写属性,多了两个方法,一个是beforeChange,一个是afterChange,若beforeChange返回值为false,那么此次set无效,返回true时方可生效,而afterChange则是在set成功后调用,beforeChange默认返回时true。
可以看到,observable提供了一个类型为ObservableProperty的对象,并重写了afterChange方法,交给了传入的onChange处理。也就是说,每次set完成后,都会调用传入onChange发出通知。
5. Delegates.vetoable() 1 2 3 4 5 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) }
看过上看的说明,这个就很容易理解了,重写了beforeChange,用传入的函数的返回值来决定本次set是否生效。
6. 原理 咋一看上去这种写法很高级,让咱们来编译成Java代码,看看这层层面纱之下,藏着的到底是什么。
1 2 3 4 class Teacher { var name: String by Delegates.notNull() var age: Int by Delegates.notNull() }
编译成java之后的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public final class Teacher { static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Teacher.class), "name" , "getName()Ljava/lang/String;" )), (KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Teacher.class), "age" , "getAge()I" ))}; @NotNull private final ReadWriteProperty name$delegate; @NotNull private final ReadWriteProperty age$delegate; @NotNull public final String getName () { return (String)this .name$delegate.getValue(this , $$delegatedProperties[0 ]); } public final void setName (@NotNull String var1) { Intrinsics.checkParameterIsNotNull(var1, "<set-?>" ); this .name$delegate.setValue(this , $$delegatedProperties[0 ], var1); } public final int getAge () { return ((Number)this .age$delegate.getValue(this , $$delegatedProperties[1 ])).intValue(); } public final void setAge (int var1) { this .age$delegate.setValue(this , $$delegatedProperties[1 ], var1); } public Teacher () { this .name$delegate = Delegates.INSTANCE.notNull(); this .age$delegate = Delegates.INSTANCE.notNull(); } }
分析
不管是代理的setValue还是getValue,其中都有一个KProperty类型的参数,所以每个属性都需要一个对应的KProperty对象
除了KProperty,每个属性,都需要一个代理对象,这里是ReadWriteProperty
每个属性,都有一个setter,和一个getter
setter中,调用代理对象的setValue方法
getter中,调用代理对象的getValue方法
总结 我的感觉,如果能直接以最终编译后Java代码来看属性代理,它反倒是增加了不少代码,如果需要什么额外的操作,完全可以直接写在属性的setter或者getter里。语法糖的好处就是,可以写一个notNull的类型,然后在所有不为空的地方使用,可以不用在每一个地方都加上判断代码,提升了效率。