前言
本文章只是用于記錄學習俩由,所以部分地方如果有錯誤或者理解不對的地方恩沽,麻煩請指正。本篇為 csdn 原文章 轉移修改版 原文章
簡述:
- lateinit 的使用
- 委托 和 委托屬性
- 標準委托 和延遲委托
1. lateinit
??在我們java 中我們在定義一個對象的時候通常可以不進行賦值
private int index;
但是在 kotlin 中如果我們同樣這樣寫 就會提示我們應該進行一個初始化糯钙,當然我們也可以通過 latelnit 來設置為 該對象是 稍后初始化的
var index: int // 報錯
var index :int = 1 // 直接初始化完成
var latelnit index :int // 稍后初始化
2. Kotlin 委托
??委托模式是軟件設計模式中的一項基本技巧。在委托模式中退腥,有兩個對象參與處理同一個請求任岸,接受請求的對象將請求委托給另一個對象來處理。
??Kotlin 直接支持委托模式狡刘,更加優(yōu)雅享潜,簡潔。Kotlin 通過關鍵字 by 實現(xiàn)委托嗅蔬。
??委托模式已經(jīng)證明是實現(xiàn)繼承的一個很好的替代方式剑按, 而 Kotlin 可以零樣板代碼地原生支持它。 Derived 類可以通過將其所有公有成員都委托給指定對象來實現(xiàn)一個接口 Base
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
// 如果沒有委托我們就需要重寫該接口
class Derived(b: Base) : Base {
override fun print() {
TODO("not implemented")
}
}
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print()
}
2.1 覆蓋由委托實現(xiàn)的接口成員
??覆蓋符合預期:編譯器會使用 override 覆蓋的實現(xiàn)而不是委托對象中的澜术。如果將 override fun print() { print("abc") } 添加到 Derived艺蝴,那么當調用 print 時程序會輸出“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(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).printMessage() // 打印出 abc
Derived(b).printMessageLine() // 沒有被覆蓋 會打印 10
}
3. 委托屬性
有一些常見的屬性類型,雖然我們可以在每次需要的時候手動實現(xiàn)它們鸟废, 但是如果能夠為大家把他們只實現(xiàn)一次并放入一個庫會更好猜敢。例如包括:
- 延遲屬性(lazy properties): 其值只在首次訪問時計算;
- 可觀察屬性(observable properties): 監(jiān)聽器會收到有關此屬性變更的通知盒延;
- 把多個屬性儲存在一個映射(map)中缩擂,而不是每個存在單獨的字段中。
為了涵蓋這些(以及其他)情況添寺,Kotlin 支持 委托屬性:
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.")
}
}
fun main(args: Array<String>) {
val e = Example()
println(e.p)
e.p = "ymc"
}
輸出結果
Example@300ffa5d, thank you for delegating 'p' to me!
ymc has been assigned to 'p' in Example@300ffa5d.
3.1 標準委托
延遲委托
語法:val/var <屬性名>: <類型> by <表達式>
??屬性的委托不必實現(xiàn)任何的接口胯盯,但是需要提供一個 getValue() 函數(shù)(和 setValue()——對于 var 屬性),因為屬性對應的 get()(和 set())會被委托給它的 getValue() 和 setValue() 方法计露。也可以直接繼承 ReadWriteProperty 博脑,實現(xiàn)其中的方法楞捂,這樣就避免了自己手寫可能出現(xiàn)的錯誤,例如 Kotlin 源碼中這樣實現(xiàn)判空的委托屬性:
public object Delegates {
public fun <T: Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
}
private class NotNullVar<T: Any>() : ReadWriteProperty<Any?, T> {
private var value: T? = null
public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
}
public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
??其中 NotNullVar 繼承了 ReadWriteProperty趋厉,并實現(xiàn)了他的兩個方法寨闹,而Delegates.notNull() 屬于委托屬性。
3.2 委托要求
(一臉懵逼的地方)
??對于一個只讀屬性(即 val 聲明的)君账,委托必須提供一個名為 getValue 的函數(shù)繁堡,該函數(shù)接受以下參數(shù)(可以繼承 ReadOnlyProperty 實現(xiàn)該方法):
thisRef —— 必須與 屬性所有者 類型(對于擴展屬性——指被擴展的類型)相同或者是它的超類型,
property —— 必須是類型 KProperty<*> 或其超類型乡数,
-
對于一個可變屬性(即 var 聲明的)椭蹄,委托必須額外提供一個名為 setValue 的函數(shù),該函數(shù)接受以下參數(shù)(可以繼承 ReadWriteProperty 實現(xiàn)該方法):
thisRef —— 同 getValue()净赴,
property —— 同 getValue()绳矩,
new value —— 必須和屬性同類型或者是它的超類型。
??getValue() 或/和 setValue() 函數(shù)可以通過委托類的成員函數(shù)提供或者由擴展函數(shù)提供玖翅。 當你需要委托屬性到原本未提供的這些函數(shù)的對象時后者會更便利翼馆。 兩函數(shù)都需要用 operator 關鍵字來進行標記。
3.3 翻譯規(guī)則
??在每個委托屬性的實現(xiàn)的背后金度,Kotlin 編譯器都會生成輔助屬性并委托給它应媚。 例如,對于屬性 prop猜极,生成隱藏屬性 prop$delegate中姜,而訪問器的代碼只是簡單地委托給這個附加屬性:
class C {
var prop: Type by MyDelegate()
}
// 這段是由編譯器生成的相應代碼:
class C {
private val prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
??Kotlin 編譯器在參數(shù)中提供了關于 prop 的所有必要信息:第一個參數(shù) this 引用到外部類 C 的實例而 this::prop 是 KProperty 類型的反射對象,該對象描述 prop 自身跟伏。
4. 標準委托
4.1延遲委托
??lazy() 是接受一個 lambda 并返回一個 Lazy 實例的函數(shù)丢胚,返回的實例可以作為實現(xiàn)延遲屬性的委托: 第一次調用 get() 會執(zhí)行已傳遞給 lazy() 的 lambda 表達式并記錄結果, 后續(xù)調用 get() 只是返回記錄的結果受扳。
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
// 輸出:
computed! // 只有第一次 初始化
Hello
Hello
??默認情況下携龟,對于 lazy 屬性的求值是同步鎖的(synchronized):該值只在一個線程中計算,并且所有線程會看到相同的值辞色。如果初始化委托的同步鎖不是必需的骨宠,這樣多個線程可以同時執(zhí)行浮定,那么將 LazyThreadSafetyMode.PUBLICATION 作為參數(shù)傳遞給 lazy() 函數(shù)相满。 而如果你確定初始化將總是發(fā)生在單個線程,那么你可以使用 LazyThreadSafetyMode.NONE 模式桦卒, 它不會有任何線程安全的保證以及相關的開銷立美。
4.2 可觀察屬性 Observable
??Delegates.observable() 接受兩個參數(shù):初始值 與 修改時處理程序(handler)。 每當我們給屬性賦值時會調用該處理程序(在賦值后執(zhí)行)方灾。它有三個參數(shù):被賦值的屬性建蹄、舊值與新值:
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
4.3 屬性保存在映射中
??一個常見的用例是在一個映射(map)里存儲屬性的值碌更。 這經(jīng)常出現(xiàn)在像解析 JSON 或者做其他“動態(tài)”事情的應用中。 在這種情況下洞慎,你可以使用映射實例自身作為委托來實現(xiàn)委托屬性痛单。
// 由于 Map只讀,我們如果需要 var 可以使用 MutableMap 替換
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
fun main(args: Array<String>) {
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
}