定义委托属性
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
}