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编译器所生成的辅助属性。

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