Kotlin学习之类与对象篇—委托属性

定义委托属性

class Example {
    var p: String by Delegate()
}

委托属性的定义语法是:val/var <属性名>: <类型> by <表达式>。by 后面的表达式就是委托, 属性的get()/set()将被委托给它的getValue()setValue()方法。属性委托不必实现任何接口,但是它们必须提供getValue()方法(如果是var属性还必须提供setValue()方法)。

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.")
    }
}

当我们从委托给Delegate实例的属性p读取值时,Delegate的getValue()方法就会被调用,它的第一个参数是我们读取p的对象,第二个参数是p自身的描述。

val e = Example()
println(e.p)

//打印出来的内容为:
Example@33a17727, thank you for delegating ‘p’ to me!

同样,当我们赋值给p,setValue()方法就会被调用,其前两个参数和getValue()一样,第三个参数为即将被赋予的值:

e.p = "NEW"

上述代码给p赋值NEW,将打印出:

NEW has been assigned to ‘p’ in Example@33a17727.


标准委托

Kotlin标准库提供了几个有用的委托方法

Lazy

lazy()是一个接收一个lambda并返回一个lazy<T>实例的方法,返回的实例能作为实现了lazy属性的委托:初次调用get()时执行lambda并记住其结果,当再次调用get()时就只返回结果。

val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

fun main(args: Array<String>) {
    println(lazyValue)
    println(lazyValue)
}

上述代码将打印出:

computed!
Hello
Hello

第一次调用lazyValue时,会执行lambda中的语句,打印出computed!,并返回结果hello,再次调用时不会执行lambda,只返回结果hello

默认情况下,对lazy属性的求值是同步锁(synchronized)的:值只在一个线程中计算,所有的线程都会看到同一个值。如果不要求初始化委托的同步锁,以便多个线程可以并发执行它,那么可以给lazy()方法传一个参数LazyThreadSafetyMode.PUBLICATION。如果可以确定初始化总是发生在单一线程中,那么可以使用LazyThreadSafetyMode.NONE模式,它不会有任何线程安全保障及相关的开销。

Observable

Delegates.observable()接受两个参数:一个初始化值 和 一个修改处理器。每当我们给属性赋值时(在赋值执行之后),处理器就会被调用。处理器有三个参数,被赋值的属性、原有值以及新值。

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main(args: Array<String>) {
    val user = User()
    user.name = "first"
    user.name = "second"
}

打印结果如下:

<no name> -> first
first -> second

如果想在赋值执行之前拦截它并决定是否执行,可以使用vetoable()方法来替代observable()。vetoable()的处理器会在赋值执行之前被调用。

var max: Int by Delegates.vetoable(0) { 
    property, oldValue, newValue ->
        newValue > oldValue
}

println(max) // 0
max = 10
println(max) // 10
max = 5
println(max) // 10

在处理器中返回一个Boolean值,如果返回true,表示赋值将会被执行;如果返回false,则不执行。

在Map中保存属性

一个常见的用例是在map里存储属性的值。这经常出现在像解析 JSON 或者做其他“动态”事情的应用中。在这种情况下,可以使用map实例作为委托属性的委托

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int by map
}

下面这个例子中,构造函数接受一个map

val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))

代理属性从map中取值(通过字符串key–属性的名字)

println(user.name) // Prints "John Doe"
println(user.age)  // Prints 25

这种方法对于var属性也起作用,如果用MutableMap替代只读的Map的话:

class MutableUser(val map: MutableMap<String, Any?>) {
    var name: String by map
    var age: Int by map
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章