美文网首页kotlin
kotlin属性代理

kotlin属性代理

作者: 请输入妮称 | 来源:发表于2018-08-15 17:46 被阅读0次

    kotlin属性代理官方文档地址:http://kotlinlang.org/docs/reference/delegated-properties.html

    kotlin的代理模式需要实现一个接口,而属性代理只需要实现getValue和setValue方法即可。

    语法形式:val/var <property name>: <Type> by <expression>
    其中<expression>代理了 <property name>的getValue和setValue方法。

    一.自定义属性代理

    举个🌰:

    class Example {
        var p: String by Delegate()
    }
    class Delegate {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            return "$thisRef, thank you for delegating '${property.name}' to me!"
        }
     
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("$value has been assigned to '${property.name}' in $thisRef.")
        }
    }
    fun main(args: Array<String>) {
        val e = Example()
        println(e.p)
    }
    

    输出:Example@33a17727, thank you for delegating ‘p’ to me!

    当我们给p赋值的时候,setValue会被调用

    e.p = "NEW"
    

    输出:NEW has been assigned to ‘p’ in Example@33a17727.

    自定义属性代理也可以实现ReadWriteProperty、ReadOnlyProperty接口。

    public interface ReadOnlyProperty<in R, out T> {
        /**
         * Returns the value of the property for the given object.
         * @param thisRef the object for which the value is requested.
         * @param property the metadata for the property.
         * @return the property value.
         */
        public operator fun getValue(thisRef: R, property: KProperty<*>): T
    }
    
    /**
     * Base interface that can be used for implementing property delegates of read-write properties.
     *
     * This is provided only for convenience; you don't have to extend this interface
     * as long as your property delegate has methods with the same signatures.
     *
     * @param R the type of object which owns the delegated property.
     * @param T the type of the property value.
     */
    public interface ReadWriteProperty<in R, T> {
        /**
         * Returns the value of the property for the given object.
         * @param thisRef the object for which the value is requested.
         * @param property the metadata for the property.
         * @return the property value.
         */
        public operator fun getValue(thisRef: R, property: KProperty<*>): T
    
        /**
         * Sets the value of the property for the given object.
         * @param thisRef the object for which the value is requested.
         * @param property the metadata for the property.
         * @param value the value to set.
         */
        public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
    }
    
    

    二.lazy

    当变量第一次被使用时才进行初始化,可以实现懒加载。

    lazy方法返回一个Lazy<T>对象,入参是一个lambda。

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

    当第一次获取lazyValue的值时,会先初始化lazyValue,然后把值保存起来,下一次获取lazyValue时直接获取保存好的值即可。

    val lazyValue: String by lazy {
        println("computed!")
        "Hello"
    }
    
    fun main(args: Array<String>) {
        println(lazyValue)
        println(lazyValue)
    }
    

    输出:

    computed!
    Hello
    Hello
    

    初始化lazyValue的过程默认是线程安全的,通过synchronized锁来保证。

    不过这肯定是影响性能的,如果我们确信lazyValue的初始化不会涉及到多线程,那么我们可以传入LazyThreadSafetyMode.NONE来取消同步锁。

    val lazyValue: String by lazy(LazyThreadSafetyMode.NONE) {
        println("computed!")
        "Hello"
    }
    

    LazyThreadSafetyMode有三种模式:SYNCHRONIZED(默认模式)、PUBLICATION、NONE

    其中PUBLICATION模式使用了AtomicReferenceFieldUpdater(原子操作)允许多个线程同时调用初始化流程。

    三.observable

    变量被赋值时会发出通知。
    Delegates.observable可以传入两个参数,一个是初始值,另一个是变量被赋值时的handle方法。

    import kotlin.properties.Delegates
    
    class User {
        var name: String by Delegates.observable("<no name>") {
            prop, old, new ->
            println("$old -> $new")
        }
    }
    
    fun main(args: Array<String>) {
        val user = User()
        user.name = "first"
        user.name = "second"
    }
    

    输出:

    <no name> -> first
    first -> second
    

    注意:只要 user.name被赋值,监听事件就会触发。

    类似的还有 vetoable(),只不过vetoable是在赋值前触发,observable是在赋值后触发。

    vetoable还可以对赋值操作进行拦截。

    import kotlin.properties.Delegates
    
    class User {
        var name: String by Delegates.vetoable("<no name>") { p, oldValue, newValue ->
            if (newValue == "first"){
                return@vetoable true // 返回true表示first可以赋值给name
            }
            return@vetoable false // 返回false表示拦截其他赋值操作。
        }
    }
    
    fun main(args: Array<String>) {
        val user = User()
        user.name = "first"
        println(user.name)
        user.name = "two"
        println(user.name)
    }
    

    输出:

    first
    first
    

    四.对map的代理

    把map中某个key值映射为变量。

    fun main(args: Array<String>) {
        val map = mutableMapOf("name" to "小明", "age" to 1)
        var name: String by map
        println(name)
        name = "小红"
        println(map["name"])
    }
    

    输出:

    小明
    小红
    

    五.局部变量的代理(1.1版本新增)

    fun example(computeFoo: () -> Foo) {
        val memoizedFoo by lazy(computeFoo)
    
        if (someCondition && memoizedFoo.isValid()) {
            memoizedFoo.doSomething()
        }
    }
    

    六.提供代理(1.1版本新增)

    我们可以干预代理类的创建过程。提供provideDelegate方法即可,该方法返回要创建的代理对象。

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

    provideDelegate方法有两个参数,参数1是代理属性所在的类,即image所在的类:MyUI。参数2是代理属性的具体参数对像(包括属性名称,属性类型等等)。

    相关文章

      网友评论

        本文标题:kotlin属性代理

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