Kotlin的by 委托
1. by lazy的原理解析
我们用kotlin经常会用到by lazy
,所以我之前一直以为这俩是必须一起用的,但其实by
跟lazy
是拆开的,像下面这段代码:
class By {
val tag by lazy {
"hello"
}
}
可以按照下面的格式来理解上面的代码
val / var : by
kotlin转成java之后的代码如下:
@Metadata(
mv = {1, 1, 16},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002 ... "},
d2 = {"Ldelegate/By;", "", "()V", "tag", "", "getTag", "()Ljava/lang/String;", "tag$delegate", "Lkotlin/Lazy;", "TestKotlin"}
)
public final class By {
static final KProperty[] $$delegatedProperties = ...
@NotNull
private final Lazy tag$delegate;
@NotNull
public final String getTag() {
Lazy var1 = this.tag$delegate;
KProperty var3 = $$delegatedProperties[0];
return (String)var1.getValue();
}
public By() {
this.tag$delegate = LazyKt.lazy((Function0)null.INSTANCE);
}
}
. 在tag$delegate
属性后面有后缀$delegate
. 注意tag$delegate
的类型是Lazy,而不是String
. 在By
构造方法里面把LazyKt.lazy
赋值给tag$delegate
. 所以getTag
返回值为给定的代码块的执行结果
感觉还是不是很清晰,继续从Kotlin源码角度看Lazy
的实现逻辑:
public actual fun lazy(initializer: () -> T): Lazy = SynchronizedLazyImpl(initializer)
private class SynchronizedLazyImpl(initializer: () -> T, lock: Any? = null) : Lazy, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
}
实现逻辑如下:
- 默认为
SynchronizedLazyImpl
实现,initializer是lambda表达式,比如最上面的Lazy的lambda为{"hello"}
- 默认值赋值为
UNINITIALIZED_VALUE
,如果不为默认值则直接返回,说明已经赋值过了。 - 后面的赋值方法由
Synchronized
包裹,支持多线程访问,先检查是否为UNINITIALIZED_VALUE
,不为默认值直接返回 - 未赋值过,先执行initializer,返回值赋值给typedValue,同时
initializer = null
,然后返回结果value
这里涉及到的几个知识点,简单介绍一下:
1.1. 如何查看Kotlin生成的java代码
1.2. 这里的Metadata是干啥的
每个kotlin生成java的类里面都会有这样的一个Metadata
的注解
@Metadata(
...
)
kotlin官网对此有介绍,Metadata
This annotation is present on any class file produced by the Kotlin compiler and is read by the compiler and reflection. Parameters have very short JVM names on purpose: these names appear in all generated class files, and we'd like to reduce their size.
1.3. lambda表达式
lambda表达式是Kotlin的很重要的一个内容,简单介绍一下上面源码中涉及到的内容,抛砖引玉,有兴趣可以详细了解。
上面的源码中用到了这么一句:
fun lazy(initializer: () -> T)
表明传入了一个lambda表达式,需要无参并返回T
。下面列出一些常用的简单lambda函数类型
//无参、无返回值的函数类型(Unit 返回类型不可省略)
() -> Unit
//接收T类型参数、无返回值的函数类型
(T) -> Unit
//接收T类型和A类型参数、无返回值的函数类型(多个参数同理)
(T,A) -> Unit
//接收T类型参数,并且返回R类型值的函数类型
(T) -> R
//接收T类型和A类型参数、并且返回R类型值的函数类型(多个参数同理)
(T,A) -> R
如何得到lambda的结果呢?上面的代码
val typedValue = initializer!!()
使用了()
得到执行的结果,也可以使用initializer!!.invoke()
得到执行结果。
2. by - 委托
Kotlin 直接支持委托模式,更加优雅,简洁,通过关键字 by 实现委托。kotlin中的委托大致有下面几种形式:
类委托
类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。
// 定义一个接口
interface Base {
fun print()
}
// 实现这个接口
class RealImpl(val x: Int) : Base {
override fun print() {
print(x)
}
}
// 实现类委托
class Delegate(b: Base) : Base by b
上面是最简单的类委托的实现:
- 定义一个接口Base
- 创建一个类RealImpl实现接口
- 创建一个类Delegate,使用关键字
by
,表明Deledate相关的属性方法都委托传入的b
来实现
看起来迷迷糊糊的,我们看一下Delegate的java代码实现:
public final class Delegate implements Base {
// $FF: synthetic field
private final Base $$delegate_0;
public Delegate(@NotNull Base b) {
Intrinsics.checkParameterIsNotNull(b, "b");
super();
this.$$delegate_0 = b;
}
public void print() {
this.$$delegate_0.print();
}
}
一目了然,传入的b
赋值给$$delegate_0
,Delegate类完全是调用了$$delegate_0
的方法来实现自己的方法。
属性委托
属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理。
val/var <属性名>: <类型> by <表达式>
by 关键字之后的表达式就是委托, 属性的 get() 方法(以及set() 方法)将被委托给这个对象的 getValue() 和 setValue() 方法。属性委托不必实现任何接口, 但必须提供 getValue() 函数(对于 var属性,还需要 setValue() 函数)。该类需要包含 getValue() 方法和 setValue() 方法,且参数 thisRef 为进行委托的类的对象,prop 为进行委托的属性的对象。
import kotlin.reflect.KProperty
// 定义包含属性委托的类
class Example {
var p: String by Delegate()
}
// 委托的类
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, 这里委托了 ${property.name} 属性"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$thisRef 的 ${property.name} 属性赋值为 $value")
}
}
fun main(args: Array) {
val e = Example()
println(e.p) // 访问该属性,调用 getValue() 函数
e.p = "Runoob" // 调用 setValue() 函数
println(e.p)
}
//Example@433c675d, 这里委托了 p 属性
//Example@433c675d 的 p 属性赋值为 Runoob
//Example@433c675d, 这里委托了 p 属性
需要注意的是,getValue和setValue的参数是固定的。
标准委托
lazy 上面介绍过了,跳过
NotNull
notNull 适用于那些无法在初始化阶段就确定属性值的场合,如果属性在赋值前就被访问的话则会抛出异常。
用法如下:
class NotNull {
class User {
val id: Int by Delegates.notNull()
}
}
反编译成java代码:
public static final class User {
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(NotNull.User.class), "id", "getId()I"))};
@org.jetbrains.annotations.NotNull
private final ReadWriteProperty id$delegate;
public final int getId() {
return ((Number)this.id$delegate.getValue(this, $$delegatedProperties[0])).intValue();
}
public User() {
this.id$delegate = Delegates.INSTANCE.notNull();
}
}
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
private class NotNullVar() : ReadWriteProperty {
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
}
}
源码简单,就是属性委托,在没赋值的时候getValue会抛出异常。
- Observable
observable 可以用于实现观察者模式,每次改变都会回调。
class Time {
var day: Int by Delegates.observable(0, { property: KProperty<*>, oldValue: Int, newValue: Int ->
println("change before: $oldValue, after: $newValue")
})
}
- Vetoable
vetoable与 observable一样,可以观察属性值的变化,不同的是,vetoable可以通过处理器函数来决定属性值是否生效。
class User {
var id: Int by Delegates.vetoable(10, { property: KProperty<*>, oldValue: Int, newValue: Int ->
// only setValue work when newValue bigger than oldValue
newValue > oldValue
})
}
上面的代码表示赋值的时候只有newValue
比oldValue
大的时候才会赋值成功。
源码上Observable
与Vetoable
类似,
public inline fun observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
ReadWriteProperty =
object : ObservableProperty(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
}
public inline fun vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
ReadWriteProperty =
object : ObservableProperty(initialValue) {
override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
}
这两个调用类似,传入初始值和lambda
,返回了一个ObservableProperty
的匿名内部类,不同的是重写的方法不一样。
observable
重写了afterChange
,vetoable
重写了beforeChange
。
继续看ObservableProperty
的源码:
public abstract class ObservableProperty(initialValue: T) : ReadWriteProperty {
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)
}
}
可以看到其实vetoable
就是用beforeChange
做了一个写属性的拦截。observable
就是做了一个afterChange
的回调。