委托属性

kotlin中除了可以委托类外,还可以委托属性。

委托属性

语法如下:

class Person {
    var name: String by Delegate()
}

而Delegate类需要实现ReadWriteProperty接口,ReadWriteProperty接口的声明如下:

public interface ReadWriteProperty<in T, V> : ReadOnlyProperty<T, V> {

    public override operator fun getValue(thisRef: T, property: KProperty<*>): V

    public operator fun setValue(thisRef: T, property: KProperty<*>, value: V)
}

如果是只读属性(val属性),那么需要实现ReadOnlyProperty接口,ReadOnlyProperty接口的声明如下:

public fun interface ReadOnlyProperty<in T, out V> {

    public operator fun getValue(thisRef: T, property: KProperty<*>): V
}

当然Delegate类不一定要实现ReadWriteProperty和ReadOnlyProperty接口,只需要保持方法的签名一直就行。

Delegate类的代码如下:

class Delegate : ReadWriteProperty<Person, String> {
    override fun setValue(thisRef: Person, property: KProperty<*>, value: String) {
        println("${thisRef}, ${property.name}, $value")
    }

    override fun getValue(thisRef: Person, property: KProperty<*>): String {
        println("${thisRef}, ${property.name}")
        return "welcome"
    }
}

这样对Person.name的读操作就会委托给Delegate.getValue(),对Person.name的写操作就会委托给Delegate.setValue()。

延迟属性

延迟属性会在第一次被访问时才会计算,然后缓存起来供后续调用使用。

val lazyValue: String by lazy {
    println("invoked")
    "hello"
}

fun main() {
    println(lazyValue)
    println(lazyValue)
}

lazy关键字其底层其实是一个函数,源代码如下,还有个重载的方法:

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
    when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

mode参数有下面三种取值:

  • LazyThreadSafetyMode.SYNCHRONIZED:默认情况下,延迟属性的计算是同步的,值只会在一个线程中得到计算,所有的线程都会使用相同的结果。
  • LazyThreadSafetyMode.PUBLICATION:如果不需要初始化延时属性的同步,这样多个线程就可以同时执行。
  • LazyThreadSafetyMode.NONE:如果确定初始化操作只会在一个线程中执行,这样就能减少线程安全方面的开销。

非空属性

notNull适用于那些无法在初始化阶段就确定属性值的场合。

class A {
    var name by Delegates.notNull<String>()
}

可观测属性

可观测属性可以在属性的值发生变化时进行监听。

Delegates.observable在值发生变化后触发:

class B {
    var name by Delegates.observable("hello") { property, oldValue, newValue ->
        println("property: ${property.name}, oldValue: $oldValue, newValue: $newValue")
    }
}

Delegates.observable在值发生变化前触发,返回true就会设置属性,返回false不会设置属性:

class C {
    var name by Delegates.vetoable("hi") { property, oldValue, newValue ->
        println("property: ${property.name}, oldValue: $oldValue, newValue: $newValue")
        true
    }
}

map属性

通常在json中,我们将属性和值存在map中,可以将map作为属性的委托。

map只读属性:

class D(map: Map<String, Any>) {
    val name: String by map
    val age: Int by map
}

fun main() {

    var map = mapOf(
        "name" to "morris",
        "age" to 20
    )
    var d = D(map)
    println(d.name) // morris
    println(d.age) // 20
}

map读写属性:

class E(map: MutableMap<String, Any>) {
    var name: String by map
    var age: Int by map
}

fun main() {
    var mutableMap: MutableMap<String, Any> = mutableMapOf(
        "name" to "morris",
        "age" to 20
    )
    var e = E(mutableMap)
    println(e.name) // morris
    println(e.age) // 20

    mutableMap["name"] = "Marry"
    println(e.name) // Marry
}

注意对map中属性的修改会体现到对象的属性上。

委托另一个属性

class F(name: String) {
    var newName: String = name
    var oldName: String by this::newName
}

委托的属性可以是以下三种:

  1. 一个顶级属性(不包含在类中)
  2. 同一个类中的成员或者扩展属性
  3. 其他类中的成员或者扩展属性

转换规则

kotlin编译器为每个委托属性提供了一个辅助的属性,对原属性的访问都会委托给这个辅助属性。例如对属性prop来说,kotlin编译器会生成辅助属性propdelegate,对prop的访问都会委托给propdelegate,对prop的访问都会委托给propdelegateprop访propdelegate。

提供委托

通过定义providerDelegate operator,我们可以扩展委托的创建逻辑过程,如果对象定义了providerDelegate方法,那么该方法就会用来创建属性委托实例。

package com.morris.kotlin.delegationpropertiest

import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

class PeopleDelegate: ReadOnlyProperty<People, String> {
    override fun getValue(thisRef: People, property: KProperty<*>): String {
        return "morris"
    }
}

class PeopleLauch {
    operator fun provideDelegate(thisRef: People,prop: KProperty<*>): ReadOnlyProperty<People, String> {
        when(prop.name) {
            "name","address" -> return PeopleDelegate()
            else -> throw Exception("error property")
        }
    }
}

class People {
    val name : String by PeopleLauch()
    val address: String by PeopleLauch()
}

fun main() {
    var people = People()
    println(people.name) // morris
}
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐