1 類委托
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 輸出 10
}
Derived 的超類型列表中的 by句表示b 將會(huì)在 Derived 中內(nèi)部存儲(chǔ)窖逗。 并且編譯器將成轉(zhuǎn)發(fā)給 b 的所有 Base 的法伞芹。
2 委托屬性
class Example {
var p: String by Delegate()
}
語法是: val/var <屬性名>: <類型> by <表達(dá)式>倚评。
屬性對應(yīng)的 get()(和 set() )會(huì)被委托給表達(dá)式的getValue() 和 setValue()散休。
第一個(gè)參數(shù)是 p 所在對象的引用次屠、第二個(gè)參數(shù)保存了對 p屬性自身的描述园匹;
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 聲明的),委托必須提供名為 getValue 的函數(shù)劫灶,該函數(shù)接受以下參數(shù):
thisRef 必須與屬性所有者類型(對于擴(kuò)展屬性指被擴(kuò)展的類型)相同或者是它的超類型裸违。
property 必須是類型 KProperty<*>或其超類型。
這個(gè)函數(shù)必須返回與屬性相同的類型(或其類型)本昏。
對于可變屬性(即 var 聲明的)供汛,委托必須額外提供 setValue 的函數(shù),該函數(shù)接受以下參數(shù):
thisRef 同 getValue() 涌穆;
property 同 getValue() 怔昨;
new value 必須和屬性同類型或者是它的超類型。
getValue() 或/和 setValue() 函數(shù)可以通過委托類的成員函數(shù)提供或者由擴(kuò)展函數(shù)提供蒲犬。 兩函數(shù)都需要 operator 關(guān)鍵字來標(biāo)記朱监。
委托類可以實(shí)現(xiàn)包含所需 operator 方法的 ReadOnlyProperty 或 ReadWriteProperty。
這倆接口是在 Kotlin 標(biāo)準(zhǔn)庫中聲明的:
interface ReadOnlyProperty<in R, out T> {
operator fun getValue(thisRef: R, property: KProperty<*>): T
}
interface ReadWriteProperty<in R, T> {
operator fun getValue(thisRef: R, property: KProperty<*>): T
operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
3 延遲屬性 Lazy
val lazyValue: String by lazy {
"Hello"
}
延遲加載屬性(lazy property): 屬性值只在初次訪問時(shí)才會(huì)計(jì)算;
get()會(huì)執(zhí)行l(wèi)ambda表達(dá)式并記錄結(jié)果原叮,后續(xù)的get方法將只返回結(jié)果赫编。
var類型屬性不能設(shè)置為延遲加載屬性,因?yàn)樵趌azy中并沒有setValue(…)方法奋隶。
lazy操作符是線程安全的擂送。如果在不考慮多線程問題或者想提高更多的性能,也可以使
用 lazy(LazyThreadSafeMode.NONE){ … } 唯欣。
在LazyThreadSafetyMode中聲明了幾種嘹吨,[Lazy]實(shí)例在多個(gè)線程之間同步訪問的形式:
SYNCHRONIZED:鎖定,用于確保只有一個(gè)線程可以初始化[Lazy]實(shí)例境氢。
PUBLICATION:初始化函數(shù)可以在并發(fā)訪問未初始化的[Lazy]實(shí)例值時(shí)調(diào)用幾次蟀拷,碰纬,但只有第一個(gè)返回的值將被用作[Lazy]實(shí)例的值。
NONE:沒有鎖用于同步對[Lazy]實(shí)例值的訪問; 如果從多個(gè)線程訪問實(shí)例问芬,是線程不安全的悦析。此模式應(yīng)僅在高性能至關(guān)重要,并且[Lazy]實(shí)例被保證永遠(yuǎn)不會(huì)從多個(gè)線程初始化時(shí)使用此衅。
class App : Application() {
val database: SQLiteOpenHelper by lazy {
MyDatabaseHelper(applicationContext)
}
override fun onCreate() {
super.onCreate()
val db = database.writableDatabase
}
}
4 可觀察屬性(Observable)
Delegates.observable() 函數(shù)接受兩個(gè)參數(shù):
第一個(gè)是初始化值,
第二個(gè)是屬性值變化事件的響應(yīng)器(handler).
這種形式的委托强戴,采用了觀察者模式,其會(huì)檢測可觀察屬性的變化挡鞍,當(dāng)被觀察屬性的setter()方法被調(diào)用的時(shí)候骑歹,響應(yīng)器(handler)都會(huì)被調(diào)用(在屬性賦值處理完成之后)并自動(dòng)執(zhí)行執(zhí)行的lambda表達(dá)式,同時(shí)響應(yīng)器會(huì)收到三個(gè)參數(shù):被賦值的屬性, 賦值前的舊屬性值, 以及賦值后的新屬性值墨微。
class ViewModel(val db: MyDatabase) {
var myProperty by Delegates.observable("") {
d, old, new ->
db.saveChanges(this, new)
}
}
5 Map中映射值
在像解析 JSON 或者做其他“動(dòng)態(tài)”事情中道媚。把map映射到屬性
import kotlin.properties.getValue
class Configuration(map: Map<String, Any?>) {
val width: Int by map
val height: Int by map
val dp: Int by map
val deviceName: String by map
}
conf = Configuration(mapOf(
"width" to 1080,
"height" to 720,
"dp" to 240,
"deviceName" to "mydevice"
))
6 局部委托屬性
你可以將局部變量聲明為委托屬性。
memoizedFoo 只有someCondition滿足條件欢嘿,第一調(diào)用才會(huì)初始化
fun example(computeFoo: () -> Foo) {
val memoizedFoo by lazy(computeFoo)
if (someCondition && memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
}