前言
委託模式已經證明是實現繼承的一個很好的替代方式, 而 Kotlin 可以零樣板代碼地原生支持它,接下來讓我們試試
委託
在kotlin中使用的是by關鍵字實現委託,下邊的這段代碼是kotlin官網的代碼
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
在我的理解便是通過by關鍵在標識了b便意味着吧b對象存儲在Derived類中,並且編譯器會將b的所有的方法轉發給Derived對象
覆蓋由委託實現的接口成員
覆蓋符合預期:編譯器會使用 override 覆蓋的實現而不是委託對象中的。如果將 override fun printMessage() { print(“abc”) } 添加到 Derived,那麼當調用 printMessage 時程序會輸出“abc”而不是“10”:
interface Base {
fun printMessage()
fun printMessageLine()
}
class BaseImpl(val x: Int) : Base {
override fun printMessage() { print(x) }
override fun printMessageLine() { println(x) }
}
class Derived(b: Base) : Base by b {
override fun printMessage() { print("abc") }
}
fun main() {
val b = BaseImpl(10)
Derived(b).printMessage()
Derived(b).printMessageLine()
}
但請注意,以這種方式重寫的成員不會在委託對象的成員中調用 ,委託對象的成員只能訪問其自身對接口成員實現:
委託屬性
有一些常見的屬性類型,雖然我們可以在每次需要的時候手動實現它們, 但是如果能夠爲大家把他們只實現一次並放入一個庫會更好。例如包括
延遲屬性(lazy properties): 其值只在首次訪問時計算;
lazy() 是接受一個 lambda 並返回一個 Lazy 實例的函數,返回的實例可以作爲實現延遲屬性的委託: 第一次調用 get() 會執行已傳遞給 lazy() 的 lambda 表達式並記錄結果, 後續調用 get() 只是返回記錄的結果。
val lazyValue :String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
可觀察屬性(observable properties): 監聽器會收到有關此屬性變更的通知;
Delegates.observable() 接受兩個參數:初始值與修改時處理程序(handler)。 每當我們給屬性賦值時會調用該處理程序(在賦值後執行)。它有三個參數:被賦值的屬性、舊值與新值:
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main() {
val user = User()
user.name = "first"
user.name = "second"
}
把屬性儲存在映射中
一個常見的用例是在一個映射(map)裏存儲屬性的值。 這經常出現在像解析 JSON 或者做其他“動態”事情的應用中。 在這種情況下,你可以使用映射實例自身作爲委託來實現委託屬性。
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))