委托
委托模式已经证明是实现继承的一个很好的替代方式, 而 Kotlin 可以零样板代码地原生支持它。Derived 类可以通过将其所有公有成员都委托给指定对象来实现一个接口 Base:
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() {
print(x)
}
}
// by关键字是关键
class Deviced(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Deviced(b).print()
}
委托对象的成员只能访问其自身对接口成员实现:
interface Base {
val message: String
fun print()
}
class BaseImpl(val x: Int) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base) : Base by b {
// 在 b 的 `print` 实现中不会访问到这个属性
override val message = "Message of Derived"
}
fun main() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}
委托属性
有一些常见的属性类型,虽然我们可以在每次需要的时候手动实现它们, 但是如果能够为大家把他们只实现一次并放入一个库会更好。例如包括:
- 延迟属性(lazy properties): 其值只在首次访问时计算;
- 可观察属性(observable properties):监听器会收到有关此属性变更的通知;
- 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。
为了涵盖这些(以及其他)情况,Kotlin 支持 委托属性:
class Example {
// by 就是关键字,这时属性的set get方法会委托给by后面的表达式
var p: String by Delegate()
}
import kotlin.reflect.KProperty
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
val e = Example()
println(e.p)
输出
Example@33a17727, thank you for delegating ‘p’ to me!
e.p = "NEW"
输出
NEW has been assigned to ‘p’ in Example@33a17727.
标准委托
Kotlin 标准库为几种有用的委托提供了工厂方法。
延迟属性 Lazy
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
lazy是线程安全的是通过使用同步锁(synchronized)来保证的,使用 LazyThreadSafetyMode.NONE 模式不会有任何线程安全的保证以及相关的开销。
可观察属性 Observable
Delegates.observable() 接受两个参数:初始值与修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值与新值:
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main() {
val user = User()
user.name = "first"
user.name = "second"
}