Kotlin的委托屬性

委托屬性的語法

val/var <屬性名>:<類型> by <表達式>
在by后面的表達式是改委托抓半,因為屬性對應的get()(與set())會被委托給它的getValue()與setValue()方法扔字。屬性的委托不必實現(xiàn)任何接口囊嘉,但需要提供一個getValue()函數(shù)(var屬性還需要提供setValue()函數(shù))
例如:

fun main(){
    val e=Example()
    println(e.d)
    e.d=6

}
class Example{
    var d:Int by DelegateTest()
}
class DelegateTest{
    operator fun getValue(thisRef:Any?,property:KProperty<*>):Int{
        return 1
    }
    operator fun setValue(thisRef: Any?,property: KProperty<*>,value:Int){
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }

}

輸出的結(jié)果:

1
6 has been assigned to 'd' in com.hx.learn.Example@433c675d.

從輸出的結(jié)果可以知道,當我們從讀取實例的d的值的時候篷角,會調(diào)用委托類的getValue()函數(shù)焊刹,當我們給實例的d賦值的時候,會調(diào)用setValue()函數(shù)恳蹲。
從上面委托類的兩個函數(shù)中看到都包含了thisRefproperty兩個參數(shù)的虐块,第一個參數(shù)是讀出d的對象,第二個參數(shù)保存了對d自身的描述嘉蕾。

屬性委托的要求

  • 對于一個只讀屬性(即val聲明的)贺奠,委托必須提供一個操作函數(shù)getValue(),getValue()的函數(shù)的返回值必須與屬性相同類型(或者是其值類型)错忱,并且函數(shù)具有以下參數(shù)
  • thisRef必須與屬性所有者類型相同或者是其超類儡率。
  • property必須是類型KProperty<*>或者是其超類。
  • 對于一個可變屬性(即var聲明的)以清,委托必須額外提供一個操作函數(shù)setValue()儿普,該函數(shù)具有以下參數(shù):
  • thisRef必須與屬性所有者類型相同或者是其超類。
  • property必須是類型KProperty<*>或者是其超類掷倔。
  • value必須與屬性類型相同(或者是其超類)

標準委托

Kotlin標準庫為幾種有用的委托提供了工廠方法:

延遲屬性Lazy

lazy()是接受一個lambda并返回一個Lazy<T>實例的函數(shù)眉孩,返回的實例可以延遲屬性的委托:第一次調(diào)用get()會執(zhí)行已傳遞給lazy()的lamdba表達達式并記錄結(jié)果,后續(xù)調(diào)用get()只返回記錄的結(jié)果勒葱。

fun main(){
    val lazyValue:String by lazy{
        println("你好")
        "lazy"
    }
    println(lazyValue)
    println("-----------")
    println(lazyValue)
}

image.png

在默認的情況下浪汪,對于lazy屬性的求值是同步鎖的:該值只在前一個線程中計算,并且所有線程看到相同的值凛虽。如果初始化委托的同步鎖不是必需的死遭,這樣多個線程可以同時執(zhí)行,那么將LazyThreadSafetyMode.PUBLICATION作為參數(shù)傳遞給lazy()函數(shù)凯旋。而如果你確定初始化將總是發(fā)生在與屬性使用位于相同的線程呀潭,那么可以使用LazyThreadSafetyMode.NONE作為Lazy()函數(shù)的參數(shù)钉迷,它不會有任何線程安全的保證以及 相關(guān)的開銷。

可觀察屬性O(shè)bservable

Delegates.observable()接受兩個參數(shù):初始化與修改時處理程序蜗侈。每當我們給屬性賦值時會調(diào)用該處理程序(在賦值后執(zhí)行)篷牌。它有是三個參數(shù):被賦值的屬性,舊值與新值踏幻。
例如:

fun main(){
    var person=Person()
    person.age=15
    person.age=16

}
class Person{
    var age:Int by Delegates.observable(10){
        property, oldValue, newValue ->
          println("${property.name}:{$oldValue}->{$newValue}")
       
    }
}

結(jié)果:

age:{10}->{15}
age:{15}->{16}

observable()是在賦值之后調(diào)用處理程序的,如果想在屬性被賦新值生效前會調(diào)用處理程序可以使用vetoable()戳杀。
voteable的處理程序中返回的是布爾值该面,如果返回為ture則讓賦值生效,否則賦值不生效信卡。

委托給另一個屬性

從kotlin 1.4開始隔缀,一個屬性可以幫它的getter與setter委托給另一個屬性。該委托屬性可以是:

  • 頂層屬性
  • 同一個類的成員或擴展屬性
  • 另一個類的成員或擴展屬性

將一個屬性委托給另一個屬性傍菇,應在委托名稱中使用::限定符猾瘸。當想要以一種向后兼容的方式命名一個屬性性時,使用@Deprecated注解來注解舊的屬性,并委托其實現(xiàn)丢习。

class MyClass {
   var newName: Int = 0
   @Deprecated("Use 'newName' instead", ReplaceWith("newName"))
   var oldName: Int by this::newName
}

fun main() {
   val myClass = MyClass()
   myClass.oldName = 42
   println(myClass.newName) // 輸出42
}
將屬性儲存在映射中

常見的用例就是在一個映射(map)里面存儲的值牵触,可以使用映射實例自身作為委托來實現(xiàn)委托屬性。
例如:

fun main(){
    var person=Person(
            mapOf("name" to "Kotlin"
                    ,"age" to 10)
    )
}
class Person(map:Map<String,Any?>){ 
    val age:Int by map 
    val name:String by map
}

委托屬性會從這個映射中取值(通過字符串鍵——屬性的名稱)

 println(person.age)
 println(person.name)

 輸出:
 10
 Kotlin

如果是var屬性的咐低,需要將只讀的Map換成MutableMap揽思。

局部委托屬性
fun main(){
   example(2)
}
fun example(value:Int){
    val tempInt by lazy {
        println("第一次被調(diào)用")
        value
    }
    println(tempInt)
    println(tempInt)

}

結(jié)果:

第一次被調(diào)用
2
2

tempInt變量只有在第一才訪問時,才會執(zhí)行處理程序,之后訪問就不再執(zhí)行见擦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钉汗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鲤屡,更是在濱河造成了極大的恐慌损痰,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酒来,死亡現(xiàn)場離奇詭異卢未,居然都是意外死亡,警方通過查閱死者的電腦和手機役首,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門尝丐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人衡奥,你說我怎么就攤上這事爹袁。” “怎么了矮固?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵失息,是天一觀的道長譬淳。 經(jīng)常有香客問我,道長盹兢,這世上最難降的妖魔是什么邻梆? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮绎秒,結(jié)果婚禮上浦妄,老公的妹妹穿的比我還像新娘。我一直安慰自己见芹,他們只是感情好剂娄,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著玄呛,像睡著了一般阅懦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上徘铝,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天耳胎,我揣著相機與錄音,去河邊找鬼惕它。 笑死怕午,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的怠缸。 我是一名探鬼主播诗轻,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼揭北!你這毒婦竟也來了扳炬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤搔体,失蹤者是張志新(化名)和其女友劉穎恨樟,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疚俱,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡劝术,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了呆奕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片养晋。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖梁钾,靈堂內(nèi)的尸體忽然破棺而出绳泉,到底是詐尸還是另有隱情,我是刑警寧澤姆泻,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布零酪,位于F島的核電站冒嫡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏四苇。R本人自食惡果不足惜孝凌,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望月腋。 院中可真熱鬧蟀架,春花似錦、人聲如沸罗售。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寨躁。三九已至,卻和暖如春牙勘,著一層夾襖步出監(jiān)牢的瞬間职恳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工方面, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留放钦,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓恭金,卻偏偏與公主長得像操禀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子横腿,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354