代理能够帮助您将任务委托给其余对象,从而带来更佳的代码复用性,您能够从 咱们以前的文章 了解到更多信息。Kotlin 不只可让您经过 by 关键字轻松实现代理,还在标准库中提供了像 lazy()、observable()、vetoable() 以及 notNull() 这样的内建代理。接下来就让咱们开始了解这些内建代理的使用,以及其内部的实现原理。html
lazy()))) 函数是一个属性代理,它能够帮您在第一次访问属性时对它们进行惰性初始化。这个函数在建立昂贵对象时十分有用。git
lazy()
函数接收两个参数,LazyThreadSafetyMode
枚举值与一个 lambda 表达式。github
LazyThreadSafetyMode
用于指定初始化过程如何在不一样线程间进行同步,它的默认值是 LazyThreadSafetyMode.SYNCHRONIZED
。这意味着初始化操做是线程安全的,但代价是显式同步会对性能形成轻微影响。设计模式
lambda 表达式会在属性第一次被访问时执行,而它的值将会被存储以用于接下来的访问。api
<!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> class Person(name: String, lastname: String) { val fullname: String by lazy() { name + lastname } //… }
在查看反编译后的 Java 代码时,咱们能够看到 Kotlin 编译器为惰性 (lazy) 代理建立了一个 Lazy 类型的引用:安全
<!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> @NotNull private final Lazy fullname$delegate;
这一代理经过调用 LazyKt.lazy()
函数,并传入您定义的 lambda 表达式与线程安全模式参数来进行初始化:jvm
<!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> this.fullname$delegate = LazyKt.lazy((Function0)(new Function0() { @NotNull public final String invoke() { return name + lastname; } }));
让咱们来观察 lazy() 的源码。因为 lazy()
函数默认使用 LazyThreadSafetyMode.SYNCHRONIZED
参数,所以它将返回一个 SynchronizedLazyImpl
类型的 Lazy 对象:ide
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
当属性代理第一次被访问时,SynchronizedLazyImpl
的 getValue()
函数就会被调用,这个函数将会在一个 synchronized
块中对属性进行初始化:函数
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 } } }
这样就能保证惰性对象会以线程安全的方式初始化,但同时也引入了由 synchronized
块带来的额外开销。性能
注意: 若是您肯定资源会在单线程中被初始化,您能够向
lazy()
传入LazyThreadSafetyMode.NONE
,这样函数就不会在惰性初始化时使用synchronized
块。不过请记住,LazyThreadSafetyMode.NONE
不会改变惰性初始化的同步特性。因为惰性初始化是同步的,因此在第一次访问时仍会消耗与非惰性初始化过程相同的时间,这意味着那些初始化过程较为耗时的对象仍会在被访问时阻塞 UI 线程。1val lazyValue: String by lazy(LazyThreadSafetyMode.NONE) {“lazy”}
惰性初始化能够帮助初始化昂贵资源,但对于诸如 String 一类的简单对象,因为 lazy()
函数须要生成 Lazy
和 KProperty
这样的额外对象,反而会增长整个过程的开销。
Delegates.observable() 是另外一个 Kotlin 标准库中内建的代理。观察者模式是一种设计模式,在这一模式中,一个对象会维护一个它的从属者的列表,这些从属者即被称为观察者。对象会在它本身的状态改变时对观察者进行通知。这一模式十分契合多个对象须要在某个值发生改变时获得通知的状况,能够避免实现为从属对象周期调用和检查资源是否更新。
observable()
函数接收两个参数: 初始化值与一个当值发生改变时会被调用的监听处理器。observable()
会建立一个 ObservableProperty 对象,用于在每次 setter 被调用时执行您传给代理的 lambda 表达式。
<!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> class Person { var address: String by Delegates.observable("not entered yet!") { property, oldValue, newValue -> // 执行更新操做 } }
经过观察反编译后的 Person
类型,咱们能够看到 Kotlin 编译器生成了一个继承 ObservableProperty 的类。这个类同时也实现了一个叫作 afterChange()
的函数,这个函数持有您传递给 observable 代理的 lambda 函数。
<!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> protected void afterChange(@NotNull KProperty property, Object oldValue, Object newValue) { // 执行更新操做 }
afterChange()
函数由父 ObservableProperty
类的 setter 调用,这意味着每当调用者为 address
设置一个新的值,setter 就会自动调用 afterChange()
函数,结果就会使全部的监听器都会收到有关改变的通知。
<!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> class Person { var address: String by Delegates.observable("not entered yet!") { property, oldValue, newValue -> // 执行更新操做 } }
您还能够从反编译后的代码中看到 beforeChange()
函数的调用,observable 代理不会使用 beforeChange()
。但对于您接下来会看到的 vetoable()
,这一函数倒是功能实现的基础。
vetoable() 是一个内建代理,属性将否决权委托给它的值。与 observable()
代理相似,vetoable()
一样接受两个参数: 初始值与监听器,当任何调用者想要修改属性值时,监听器就会被调用。
<!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> var address: String by Delegates.vetoable("") { property, oldValue, newValue -> newValue.length > 14 }
若是 lambda 表达式返回 true,属性值就会被修改,反之则保持不变。在本例中,若是调用者尝试使用长度小于 15 个字符的字符串来更新地址的话,当前值就不会发生改变。
观察反编译后的 Person
能够发现,Kotlin 新生成了一个继承 ObservableProperty
的类,该类中包含了咱们传入 beforeChange()
函数的 lambda 表达式,setter 会在每次值被设置以前调用 lambda 表达式。
<!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> public final class Person$$special$$inlined$vetoable$1 extends ObservableProperty { protected boolean beforeChange(@NotNull KProperty property, Object oldValue, Object newValue) { Intrinsics.checkParameterIsNotNull(property, "property"); String newValue = (String)newValue; String var10001 = (String)oldValue; int var7 = false; return newValue.length() > 14; } }
Kotlin 标准库中所提供的最后一个内建代理是 Delegates.notNull()。notNull()
容许一个属性能够延后一段时间初始化,与 lateinit 相似。因为 notNull()
会为每一个属性建立额外的对象,因此大多数状况下推荐使用 lateinit
。不过,您能够将 notNull()
与原生类型一同使用,这点是 lateinit
所不支持的。
val fullname: String by Delegates.notNull<String>()
notNull()
使用一种特殊类型的 ReadWriteProperty
: NotNullVar。
咱们能够查看反编译后的代码,下面的例子中使用 notNull()
函数初始化了 fullname
属性:
this.fullname$delegate = Delegates.INSTANCE.notNull();
该函数返回了一个 NotNullVar
对象:
public fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
NotNullVar
类型持有一个泛型的可空内部引用,若是在初始化值以前有任何代码调用 getter,则抛出 IllegalStateException()
。
<!-- Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 --> 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 } }
有了 Kotlin 标准库提供的这组内建代理,您无需再编写、维护与从新发明这些功能。这些内建代理能够帮您惰性初始化字段、容许原生类型延迟加载、监听并在值发生改变时得到通知,甚至能够否决属性值更改。