Kotlin小记-8 委托&委托属性

300 阅读1分钟

委托

委托模式已经证明是实现继承的一个很好的替代方式, 而 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"
}