Item 21: Use property delegation to extract common property pattern
Kotlin 的屬性委託是一種強大的功能,用於提取和重用常見的屬性模式,可以用於支援程式碼的重用性。它為我們提供了一種通用的方式來提取常見的屬性行為。一個重要的例子是懶惰 (Lazy) 屬性 - 在其第一次使用時按才初始化的屬性。這樣的模式非常受歡迎,在不支持原生惰性屬性的語言中,每次需要時都必須執行一次 function, 可能會花費許多時間。在 Kotlin stdlib 中,有一個 lazy,該函數返回一個實現懶惰屬性模式的屬性委託:
val value by lazy { createValue() }
lazy 不是唯一的屬性委托。另一個重要的例子是 observable 屬性 - 一個在屬性改變時做某事的屬性。例如,假設你有一個 list adapter 會重新畫出 list。每當其中的資料發生變化時,我們需要根據資料的改變重新畫出 list。就可以利用 observable 達到
var items: List<Item> by Delegates.observable(listOf()) { _, _, _ ->
notifyDataSetChanged()
}
或者你可能需要記錄屬性的所有更改。也可以使用 observable 實現:
var key: String? by Delegates.observable(null) { _, old, new ->
Log.e("key changed from $old to $new")
}
在上面的例子中,Delegates.observable 創建了一個具有觀察者功能的屬性。每次屬性的值被修改時,給定的 lambda 函數就會被調用。lambda 函數接收三個參數:一個是屬性的元數據(如名稱)、一個是舊值和一個是新值。
這是 observable 屬性委託的一個簡單例子,但實際上,您可以在該 lambda 函數中執行任何操作,如資料驗證、通知其他系統模組等。
observable 會有點 Spring 中 AOP 的的味道,但有一個滿大的不同的是 AOP 是 runtime 時的解釋,相反的 observable 是在編譯期就決定,這增加了安全性與效能。
vetoable 是 Kotlin 的另一個有趣的屬性委託。與 observable 不同,vetoable 允許你根據某些條件接受或拒絕屬性的新值。如果 vetoable 中的 lambda 返回 true,則新值會被接受;如果返回 false,則新值會被拒絕,屬性值不會更改。
以下是一個使用 vetoable 的例子,其中我們有一個 age 屬性,並確保其值不會小於0:
import kotlin.properties.Delegates
class Person {
var age: Int by Delegates.vetoable(0) { _, _, newValue ->
// 只有當新值大於或等於0時,我們才接受該值
newValue >= 0
}
}
fun main() {
val person = Person()
println(person.age) // Prints: 0
person.age = 25
println(person.age) // Prints: 25
person.age = -5
println(person.age) // Prints: 25, 因為 -5 小於 0, 所以值未改變
}
當我們嘗試將 age 設為一個小於 0 的值時,vetoable 會阻止其更改。