定義委託屬性
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
}