Kotlin委託詳解

Kotlin 的類委託及委託屬性詳解

類委託 屬性委託
by 延遲屬性(lazy)
- 可觀測屬性(Observable)
- 非空屬性(Delegate.notnull)
- map屬性(Map<String, Any?>)
- 提供委託(provideDelegate )

類委託

class  MyDelegation{
    operator fun getValue(thisRef:Any?,protery:KProperty<*>):String="$thisRef,your delegated protery name is ${protery.name}"
    operator fun setValue(thisRef:Any?,protery:KProperty<*>,value:String)= println("$thisRef,new value is $value")
}

class MyProteryClass{
    var name:String by MyDelegation() //屬性申明必須初始化,這裏把當前屬性委託給了MyDelegation類。
}

fun main(args: Array<String>) {
    val myproteryClass=MyProteryClass()
    myproteryClass.name="Hello world" //這是設置value
    println(myproteryClass.name) //這是獲取value
}

類委託原理:by關鍵字後面的對象實際上被儲存在類的內部,編譯器則會父接口中的所有方法實現出來,並且將實現轉移給委託對象去執行。 by後面的就是委託給當前對象。

屬性委託

延遲屬性(lazy):指的是屬性只在第一次被訪問的時候纔會計算,之後則會將之前計算的結果緩存起來供後續調用。

lazy後有三種情況:

  • SYNCHRONIZED: 默認情況下延遲屬性是同步的,值只會在一個線程中得到計算,所有線程都會使用相同的結果。
  • PUBLICATION:如果不需要初始化委託的同步,這樣多個線程可以同時執行。
  • NONE:如果確定初始化操作只在一個線程中執行,這樣會減少線程安全方面的開銷。
val myLazyValue:Int by lazy(LazyThreadSafetyMode.NONE){
    println("hello world")
    30
}
fun main(args: Array<String>) {
      println(myLazyValue)
}

非空屬性(notNull):notNull 適用於那些在初始化無法確定屬性值的場合。

class MyPerson{
    var address:String by Delegates.notNull<String>()  //notNull
}
fun main(args: Array<String>) {
    val myPerson=  MyPerson()
    myPerson.address="chongqing"
    println(myPerson.address)
}

可觀測屬性(Observable):

Delegates.Observable 接收2個參數:初始值與修改處理器。處理器會在我們每次賦值時得到調用(在賦值完成後被調用)。處理器本身接收3個參數:被賦值的屬性本身,舊的屬性值與新的屬性值。
Delegates.vetoable 調用時機與Delegates.Observable相反,它是在對屬性賦值之前被調用,根據Delegates.vetoable返回的 結果是true或false來決定是否真正對屬性賦值。
class Person{
    var age:Int by Delegates.Observable(20){
        prop,oldvalue,newvalue->println("${prop.name},oldvalue:$oldvalue,newvalue:$newvalue")
    }
}
class Person2{
    var age:Int by Delegates.vetoable(20){
            prop,oldvalue,newvalue->when{
        oldvalue<=newvalue->true
        else ->false
    }
    }
}
fun main(args: Array<String>) {
    val myPerson=  Person()
    myPerson.age=30  //得到結果:age,oldvalue:20,newvalue:30
    myPerson.age=40 //得到結果:age,oldvalue:30,newvalue:40
    println("----------")
    val myPerson2=  Person2()
    println(myPerson2.age)  //得到結果:20
    myPerson2.age=40  
    println(myPerson2.age)   //得到結果:40,因爲20<40得到true,賦值成功。
    println("----------")
    myPerson2.age=30
    println(myPerson2.age)  //因爲40>30得到false,賦值失敗。新值是40了。
}

map屬性(Map):

將屬性儲存到map中,一種常見的應用場景是將屬性值儲存到map中。這通常出現在json解析或是一種動態行爲。在這種情況中,你可以將map實例作爲委託,作爲類中的屬性的委託。map中的key名字要與類中屬性名字一致。
class Student(map: Map<String, Any?>) {
    val name: String by map
    val address: String by map
    val age: Int by map
    val birthday: Date by map
}
class Student2(map: MutableMap<String, Any?>) { //可變的map
    val address: String by map
}
fun main(args: Array<String>) {
    val student = Student(
        mapof(
            "name" to "zhangsan",
            "address" to "chengdu", //必須用to
            "age" to20,
            "birthday" to Date()
        )
    )
    println(student.name)
    println(student.address)
    println(student.age)
    println(student.birthday)
    val map=MutableMapOf("address" to "chongqing")
    val student2 = Student2(map) //此時把map放進來會報Reuqerd:String,Any? found:String,String
    //修改上面報錯的問題,定義返回的類型
    val map2:MutableMap<String, Any?>=MutableMapOf("address" to "chongqing")
    val student2 = Student2(map2) 就不會報錯了
    println(map2["address"]) //chongqing
    println(student2.address)//chongqing
    println("------------")
    student2.address="chengdu"
    println(map2["address"])//chengdu
    println(student2.address)//chengdu
}

提供委託(providing a delegate)

通過定義provideDelegate operator,我們可以擴展委託的創建邏輯過程。如果對象定義了provideDelegate方法,那麼該方法就會被調用來創建屬性委託實例。
class PropertyDelegate:ReadOnlyProperty<People,String>{
    override fun getValue(thisRef:People,property:KProperty<*>):String{
        return "hello world"
    }
}

class PeopleLauncher{
    operator fun provideDelegate(thisRef:People,property:KProperty<*>):ReadOnlyProperty<People,String>{
        when(property.name){
            "name","address"->return PropertyDelegate()
            else ->throw Exception("not valid name")
        }
    }
}
class People{
    val name:String by PeopleLauncher()
    val address:String by PeopleLauncher()
}

fun main(args: Array<String>) {
  val people=People()
  println(people.name)  //輸出 hello world
  println(people.address) //輸出 hello world 
    println(people.age) // "not valid name" 因爲屬性名稱不一致
}

屬性委託的要求

對於只讀屬性來說(val修飾的屬性),委託需要提供一個名爲getvalue的方法,該方法接收如下參數:
-thisRef: 需要是屬性擁有者相同的類型或是父類型(對於擴展屬性來說,這個類型指的是被擴展的那個類型)
-property: 需要時KProperty<*>類型或是其父類型

getValue方法需要返回與屬性相同的類型或是其子類型

對於可變屬性來說(var修飾的屬性),委託需要再提高一個setvalue方法,該方法需要接收如下參數:
-thisRef: 與getvalue方法thisRef要求一致。
-property: 與getvalue方法property要求一致。
-new value:需要與屬性的類型相同或是其父類型。

getValue與setvalue方法既可以作爲委託類的成員方法實現,也可以作爲其擴展方法來實現。
這2個方法都必須要標記爲operator關鍵字,對於委託類來說,它可以實現ReadOnlyProperty或是ReadWriteProperty接口,
這些接口包含了相應的getvalue與setvalue方法,對於委託類來說,也可以不去實現這2個接口,而是自己單獨實現相應的
getvalue與setvalue方法。

委託轉換規則:

對於每個委託屬性來說,Kotlin編譯器在底層會生成一個輔助的屬性,然後將對原有屬性的訪問委託給這個輔助屬性。比如
說,對於屬性prop來說,Kotlin編譯器所生成的隱含的屬性名爲prop$delegate的屬性,然後對原有的prop屬性的
訪問器的訪問都只是委託給了這個額外的,Kotlin編譯器所生成的輔助屬性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章