Kotlin的委托属性

委托属性的语法

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实例的函数,返回的实例可以延迟属性的委托:第一次调用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){ 
    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的委托属性)