Delegated Properties

作者: lhyz | 来源:发表于2017-05-23 19:06 被阅读15次

    1.自定义代理

    class Delegate {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            return "$thisRef,thanks you for delegating"
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("$value has been assigned to in $thisRef")
        }
    }
    

    上面的示例就是针对String这个类型的属性的代理类实现,用法如下

    class Example {
        var p: String by Delegate()
    }
    

    在此类被使用的时候,对p变量的幅值和取值都会经过代理类

    val e = Example()
        e.p = "a"
        print(e.p)
    

    此时会依次打印出

    a has been assigned to in Example@79fc0f2f
    Example@79fc0f2f,thanks you for delegating
    

    2.标准代理库

    import kotlin.properties.Delegates
    

    Kotlin标准库中实现了一个代理工厂类Delegates,可以很方便的被使用,以下是官网上介绍的几种

    lazy (延迟赋值?)

    val v: String by lazy {
        println("delegate")
        "hello"
    }
    
    fun main(args: Array<String>) {
        print(v)
        print(v)
    }
    

    可以看到的是lazy实现的是一种lambda调用方法。
    lazy在变量第一次被赋值之前插入一段操作,因此最后的结果是输出了一次delegate,两次hello

    observable (观察者?)

    class User {
        var name: String by Delegates.observable("default") {
            property, oldValue, newValue ->
            println("$property $oldValue -> $newValue")
        }
    }
    
    
    fun main(args: Array<String>) {
        val user = User()
        user.name = "first"
        user.name = "second"
    }
    

    Delegates.observable定义两个参数,一个是初始值,一个就是监听值的修改的方法,使用lambda表达式的参数就是property, oldValue, newValue这三个,顾名思义即可。
    因此输出的结果是:

    var User.name: kotlin.String default -> first
    var User.name: kotlin.String first -> second
    

    3.存储属性map

    class User(map: Map<String, Any?>) {
        val name: String by map
        val age: Int     by map
    }
    
    
    fun main(args: Array<String>) {
        val user = User(mapOf(
                "name" to "john",
                "age" to 11
        ))
        println(user.name)
        println(user.age)
    }
    

    使用map存储属性键值对进行代理初始化

    4.代理类属性 总结

    要实现一个代理类所要所的事如下:

    • 对于只读属性,需要实现getValue方法,参数包括 : thisRef- 必须是所代理类型或其超类,property-必须是KProperty<*>或其超类
    • 对于可变类型,需要实现getValuesetValue方法,setValue方法除了最后一个附加参数必须是其代理类型之外,其他类型与getValue相同

    如果不清楚具体写法,可以直接通过实现标准库中的接口来定义自己的代理类:下面就是标准库中给出的接口

    interface ReadOnlyProperty<in R, out T> {
        operator fun getValue(thisRef: R, property: KProperty<*>): T
    }
    
    interface ReadWriteProperty<in R, T> {
        operator fun getValue(thisRef: R, property: KProperty<*>): T
        operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
    }
    

    翻译规则(看看就行了)

    class C {
        var prop: Type by MyDelegate()
    }
    
    // this code is generated by the compiler instead:
    class C {
        private val prop$delegate = MyDelegate()
        var prop: Type
            get() = prop$delegate.getValue(this, this::prop)
            set(value: Type) = prop$delegate.setValue(this, this::prop, value)
    }
    

    *Providing a delegate

    一个类似getValue的代理接口provideDelegate

    class ResourceLoader<T>(id: ResourceID<T>) {
        operator fun provideDelegate(
                thisRef: MyUI,
                prop: KProperty<*>
        ): ReadOnlyProperty<MyUI, T> {
            checkProperty(thisRef, prop.name)
            // create delegate
        }
    
        private fun checkProperty(thisRef: MyUI, name: String) { ... }
    }
    
    fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { ... }
    
    class MyUI {
        val image by bindResource(ResourceID.image_id)
        val text by bindResource(ResourceID.text_id)
    }
    

    原因是如果要实现相同的效果,不用此方法而是使用getValue方法的话不太优雅,例如:

    // Checking the property name without "provideDelegate" functionality
    class MyUI {
        val image by bindResource(ResourceID.image_id, "image")
        val text by bindResource(ResourceID.text_id, "text")
    }
    
    fun <T> MyUI.bindResource(
            id: ResourceID<T>,
            propertyName: String
    ): ReadOnlyProperty<MyUI, T> {
       checkProperty(this, propertyName)
       // create delegate
    }
    

    相关文章

      网友评论

        本文标题:Delegated Properties

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