属性代理(delegated properties)
在Kotlin中,我们可以不直接给属性赋值而是通过代理的方式。主要的方法有以下三种:
(1)通过delegate的方式进行赋值
(2)lazy properties,只有在第一次调用它的get方法时才会进行赋值
(3)observable properties, 对属性值得改变进行监听
- 通过代理的方式
语法格式为val/var : by , 这个property的赋值将会通过后边的expression进行赋值,expression有一个或两个方法getValue 和setValue(var时会有此方法),与property的get, set方法相对应。调用property的get方法时就会委托给expression的getValue方法,set方法也会这样。代码如下:
class Example {
var p: String by Delegate ()
}
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.
- lazy property
lazy() 是一个带有lambda参数的方法,如果是第一次初始化的时候会调用lazy()方法,之后会将lazy()方法的结果储存起来,以后再次使用这个property的时候将直接使用储存起来的值。示例代码如下:
val lazyValue: String by lazy{
println("invoke lazy fun")
"hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
//打印结果为:
computed!
hello
hello
默认情况下lazy方法是synchronized的。如果不需要使用线程同步我们可以给lazy传一个参数LazyThreadSafetyMode.PUBLICATION,如果我们非常确定将只会在一个线程中使用则我们可以传参LazyThreadSafetyMode.NONE
- observable property
我们使用的是Delegates.observable(…)方法, 它有两个参数,第一个参数时初始化值,第二个参数为lambda,这个lambda有三个参数:KProperty, oldValue, newValue。如果这个属性的值发生变化时,将会调用lambda。示例代码如下:
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
- map property
在应用中我们经常用map来存储数据,kotlin中我们可以通过map给所有的property赋值,简直简单到爆啊。示例代码如下:
class User(val map: Map<String, Any?>){
val name: String by map
val age: Int by map
}
val user = User(mapOf("name" to "zhangsan", "age" to 21))
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
如果我们的属性是var的,那我们需要使用MutableMap