Kotlin for android學(xué)習(xí)八:委托

前言

kotlin官網(wǎng)kotlin教程學(xué)習(xí)教程的筆記。

一、類的委托

背景:委托模式是類繼承模式之外的另一種很好的替代方案,kotlin直接支持委托模式。
定義:類的委托即一個(gè)類中定義的方法實(shí)際是調(diào)用另一個(gè)類的對(duì)象的方法來實(shí)現(xiàn)的。

interface CanFly{
  fun fly()
}
class Bird(f:CanFly):CanFly by f // 只有接口可以委托哦!編譯器會(huì)生成繼承自CanFly接口的所有方法,并將調(diào)用轉(zhuǎn)發(fā)給f對(duì)象

class AnimalWithWings :CanFly{
    override fun fly() {
        println("use wings to fly")
    }
}
class Bat:CanFly  by AnimalWithWings() //編譯器會(huì)生成繼承自CanFly接口的所有方法,并將調(diào)用轉(zhuǎn)發(fā)給AnimalWithWings對(duì)象
fun main(){
    Bird(AnimalWithWings()).fly()
    Bat().fly()
}

二、委托屬性

背景:具有共性的屬性,在每個(gè)需要這些屬性的類中手工實(shí)現(xiàn)比較麻煩,如果能夠只實(shí)現(xiàn)一次,供所有需要的類使用,那將會(huì)好很多。
定義:一個(gè)類的某個(gè)屬性值不在類中直接進(jìn)行定義,而是將其托付給一個(gè)代理類,從而實(shí)現(xiàn)對(duì)該類的屬性統(tǒng)一管理
語法:val/var <屬性名>: <類型> by <表達(dá)式>
官網(wǎng)上的自定義委托實(shí)現(xiàn)方式是這樣的,

class User {
    var c: String by C()
}
class C {
    operator fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
        println("$thisRef 的${property.name}屬性賦值為$value")
    }
    operator fun getValue(thisRef: Any, property: KProperty<*>): String {
        return "$thisRef,委托了${property.name}屬性"
    }
}

更快捷的實(shí)現(xiàn)方式如下:

class User {
    var c: String by C()
}
class C : ReadWriteProperty<Any, String> {
    override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
        println("${thisRef}的${property.name}屬性賦值為$value")
    }
    override fun getValue(thisRef: Any, property: KProperty<*>): String {
        return "$thisRef,委托了${property.name}屬性"
    }
}

這里看一下ReadWriteProperty類的內(nèi)容:

public interface ReadWriteProperty<in R, T> {
    public abstract operator fun getValue(thisRef: R, property: kotlin.reflect.KProperty<*>): T

    public abstract operator fun setValue(thisRef: R, property: kotlin.reflect.KProperty<*>, value: T): kotlin.Unit
}

輸出
com.example.demo.User@9bdf2f3的c屬性賦值為c
com.example.demo.User@9bdf2f3,委托了c屬性

三、標(biāo)準(zhǔn)委托

Kotlin 標(biāo)準(zhǔn)庫中提供了一些工廠方法, 可以實(shí)現(xiàn)幾種很有用的委托
1. 延遲加載

lazy()是一個(gè)函數(shù),接受一個(gè)lambda表達(dá)式作為參數(shù),返回一個(gè)Lazy<T>類型的實(shí)例,這個(gè)實(shí)例可以作為一個(gè)委托,實(shí)現(xiàn)延遲加載屬性:第一次調(diào)用 get() 時(shí), 將會(huì)執(zhí)行 lazy() 函數(shù)受到的 Lambda 表達(dá)式, 然后會(huì)記住這次執(zhí)行的結(jié)果, 以后所有對(duì) get() 的調(diào)用都只會(huì)簡(jiǎn)單地返回以前記住的結(jié)果,

  val l by lazy {
        println("computed")
        "lazy"
    }

  var user = User()
   println(user.l) // 輸出computed lazy
   println(user.l) //輸出lazy

2. 可觀察屬性

有了這個(gè),再也不用思考怎么實(shí)時(shí)監(jiān)控屬性變化了\(^o^)/~

  • Delegates.observable()

Delegates.observable() 函數(shù)接受兩個(gè)參數(shù):第一個(gè)是初始化值,第二個(gè)是屬性值變化事件的響應(yīng)器 (handler). 每次我們向?qū)傩再x值時(shí), 響應(yīng)器(handler)都會(huì)被調(diào)用(在屬性賦值處理完成 之后). 響應(yīng)器收到三 個(gè)參數(shù): 被賦值的屬性, 賦值前的舊屬性值, 以及賦值后的新屬性值

class User {
    var o by Delegates.observable("<none>") {
        kProperty: KProperty<*>, old: String, new: String ->
        println("${kProperty.name}: $old -> $new")
    }
}

 var user = User()
 println(user.o) // 輸出<none>
user.o = "first change" // 輸出o: <none> -> first change
println(user.o) // first change
 user.o = "second change" // 輸出 o: first change -> second change
println(user.o) // 輸出second change
  • Delegates.vetoable()

如果你希望能夠攔截屬性的賦值操作, 并且還能夠 “否決” 賦值操作, 那么不要使用 observable() 函數(shù), 而應(yīng)該改用 vetoable() 函數(shù). 傳遞給 vetoable 函數(shù)的事件響應(yīng)器, 會(huì)在屬性賦值處理執(zhí)行之前被調(diào)用.

    var v by Delegates.vetoable("<none>") {
        kProperty: KProperty<*>, old: String, new: String ->
        println("${kProperty.name}: $old -> $new")
        new.contains("change")
    }

      var user = User()
        println(user.v) //輸出<none>
        user.v = "first change" //輸出v: <none> -> first change
        println(user.v) //輸出first change
        user.v = "second " //輸出v: first change -> second
        println(user.v) //輸出first change
        user.v = "third change" //輸出v: first change -> third change
        println(user.v) //輸出third change
  • Delegates.notNull
    如果這個(gè)值在被獲取之前沒有被分配,它就會(huì)拋出 一個(gè)異常。
class User {
    var o by Delegates.notNull<String>()
}

user.o = "ooo"
println(user.o) //報(bào)錯(cuò),Caused by: java.lang.IllegalStateException: Property o should be initialized before get.

3. 將多個(gè)屬性保存在一個(gè)map內(nèi)
使用 map 實(shí)例本身作為屬性的委托

class User(map: MutableMap<String, Any?>) {
    var id: Long by map
    var name: String by map
}
class Teacher(map: MutableMap<String, Any?>) {
    var id: Long by map
    var name: String by map
}

       var map: MutableMap<String, Any?> = mutableMapOf("name" to "user", "id" to 1)
        var user = User(map)
        var t = Teacher(map)

        println("before change")
        println("user ->${user.id}--${user.name}")
        println("teacher->${t.id}--${t.name}")

        println(" change map id to 850")
        map.set("id", 850)
        println("user ->${user.id}--${user.name}")
        println("teacher->${t.id}--${t.name}")

        println(" change user.name to userchange")
        user.name = "userchange"
        println("user ->${user.id}--${user.name}")
        println("teacher->${t.id}--${t.name}")
        println("map->${map.entries}")
結(jié)果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容