美文网首页
Kotlin的委托属性

Kotlin的委托属性

作者: 132xin | 来源:发表于2020-11-12 18:46 被阅读0次

    委托属性的语法

    val/var <属性名>:<类型> by <表达式>
    在by后面的表达式是改委托,因为属性对应的get()(与set())会被委托给它的getValue()与setValue()方法。属性的委托不必实现任何接口,但需要提供一个getValue()函数(var属性还需要提供setValue()函数)
    例如:

    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.")
        }
    
    }
    

    输出的结果:

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

    从输出的结果可以知道,当我们从读取实例的d的值的时候,会调用委托类的getValue()函数,当我们给实例的d赋值的时候,会调用setValue()函数。
    从上面委托类的两个函数中看到都包含了thisRefproperty两个参数的,第一个参数是读出d的对象,第二个参数保存了对d自身的描述。

    属性委托的要求

    • 对于一个只读属性(即val声明的),委托必须提供一个操作函数getValue(),getValue()的函数的返回值必须与属性相同类型(或者是其值类型),并且函数具有以下参数
    • thisRef必须与属性所有者类型相同或者是其超类。
    • property必须是类型KProperty<*>或者是其超类。
    • 对于一个可变属性(即var声明的),委托必须额外提供一个操作函数setValue(),该函数具有以下参数:
    • thisRef必须与属性所有者类型相同或者是其超类。
    • property必须是类型KProperty<*>或者是其超类。
    • value必须与属性类型相同(或者是其超类)

    标准委托

    Kotlin标准库为几种有用的委托提供了工厂方法:

    延迟属性Lazy

    lazy()是接受一个lambda并返回一个Lazy<T>实例的函数,返回的实例可以延迟属性的委托:第一次调用get()会执行已传递给lazy()的lamdba表达达式并记录结果,后续调用get()只返回记录的结果。

    fun main(){
        val lazyValue:String by lazy{
            println("你好")
            "lazy"
        }
        println(lazyValue)
        println("-----------")
        println(lazyValue)
    }
    
    image.png
    在默认的情况下,对于lazy属性的求值是同步锁的:该值只在前一个线程中计算,并且所有线程看到相同的值。如果初始化委托的同步锁不是必需的,这样多个线程可以同时执行,那么将LazyThreadSafetyMode.PUBLICATION作为参数传递给lazy()函数。而如果你确定初始化将总是发生在与属性使用位于相同的线程,那么可以使用LazyThreadSafetyMode.NONE作为Lazy()函数的参数,它不会有任何线程安全的保证以及 相关的开销。
    可观察属性Observable

    Delegates.observable()接受两个参数:初始化与修改时处理程序。每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有是三个参数:被赋值的属性,旧值与新值。
    例如:

    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}")
           
        }
    }
    
    

    结果:

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

    observable()是在赋值之后调用处理程序的,如果想在属性被赋新值生效前会调用处理程序可以使用vetoable()。
    voteable的处理程序中返回的是布尔值,如果返回为ture则让赋值生效,否则赋值不生效。

    委托给另一个属性

    从kotlin 1.4开始,一个属性可以帮它的getter与setter委托给另一个属性。该委托属性可以是:

    • 顶层属性
    • 同一个类的成员或扩展属性
    • 另一个类的成员或扩展属性

    将一个属性委托给另一个属性,应在委托名称中使用::限定符。当想要以一种向后兼容的方式命名一个属性性时,使用@Deprecated注解来注解旧的属性,并委托其实现。

    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)里面存储的值,可以使用映射实例自身作为委托来实现委托属性。
    例如:

    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("第一次被调用")
            value
        }
        println(tempInt)
        println(tempInt)
    
    }
    

    结果:

    第一次被调用
    2
    2
    

    tempInt变量只有在第一才访问时,才会执行处理程序,之后访问就不再执行。

    相关文章

      网友评论

          本文标题:Kotlin的委托属性

          本文链接:https://www.haomeiwen.com/subject/nxiubktx.html