美文网首页
kotlin提供的一些属性代理

kotlin提供的一些属性代理

作者: qiHuang112 | 来源:发表于2020-03-12 14:26 被阅读0次

前言

在上一篇文章中降到了lazy{""}作为koltin的属性代理对象,在kotlin中,除了lazy还有一些其他的属性代理方式。

关于by关键字

还是先复习一下by关键字的使用方法
val(r) [变量名] by [代理对象]
EXP:val a by lazy{"huang"}
也就是说,by之后跟的是一个对象,这是一个存在getValue方法的实例对象!
如果能认识大这一点,那么by关键字就很简单了。直接来看看到底有哪些属性代理对象吧。

属性代理对象们

一共有三个,分别是下面三个

  • Delegates.notNull<T>
  • Delegates.observable<T>
  • Delegates.vetoable<T>

先解释一下是干啥的,然后再告诉你为什么。

  • Delegates.notNull<T>表示属性不为空,如果为空,则抛出异常,相当于java版本的不给初始值的属性,默认为null
  • Delegates.observable<T>表示属性可观测,在设置值的前后提供监听方法,函数入参包含了初值,所以不为空,相当于给了初值并设置了监听
  • Delegates.vetoable<T>表示属性设置成功需要满足一定的条件,这个方法返回的对象是Delegates.observable<T>的一个子对象,可以一起分析

整体看,这三个方法都返回一个ReadWriteProperty<in R, 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)
}

你看,果然实现了getValue方法,并且同时实现了setValue方法,再看看注释,这个接口就是为了给可读写属性用作代理的,简直是不要太方便啊。

看看这个接口的实现类

  • 1.NotNullVar
private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
    private var value: T? = null

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value
    }
}
  • 2.ObservableProperty
/**
 * Implements the core logic of a property delegate for a read/write property that calls callback functions when changed.
 * @param initialValue the initial value of the property.
 */
public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
    private var value = initialValue

    /**
     *  The callback which is called before a change to the property value is attempted.
     *  The value of the property hasn't been changed yet, when this callback is invoked.
     *  If the callback returns `true` the value of the property is being set to the new value,
     *  and if the callback returns `false` the new value is discarded and the property remains its old value.
     */
    protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true

    /**
     * The callback which is called after the change of the property is made. The value of the property
     * has already been changed when this callback is invoked.
     */
    protected open fun afterChange(property: KProperty<*>, oldValue: T, newValue: T): Unit {}

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        val oldValue = this.value
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        this.value = value
        afterChange(property, oldValue, value)
    }
}

在看看前面分析的kotlin提供的三种初始化方式,结构自然就出来了

  • Delegates.notNull<T> 对应 NotNullVar
  • Delegates.observable<T> 对应 ObservableProperty
  • Delegates.vetoable<T> 对应 ObservableProperty

当然,我们也可以更直接一点,自己构造一个对象,继承ObservableProperty来完成我们自己特殊的需求

var e by object : ObservableProperty<String>("hhhh") {
    override fun beforeChange(property: KProperty<*>, oldValue: String, newValue: String): Boolean {
        val flag = oldValue.length < newValue.length
        println("'$newValue'长度${if (flag) "大" else "小"}于'$oldValue', 修改${if (flag) "成功" else "失败"}!")
        return flag // 为false则允许修改
    }
    override fun afterChange(property: KProperty<*>, oldValue: String, newValue: String) {
        println("'$oldValue' 被修改为 '$newValue'!")
    }
}

下面提供我在学习的时候自己写的一个小demo,里面有一些例子,感兴趣的可以跑跑看,记得结合show kotlin bytecode,对照反编译的java代码食用更佳哦!

package delegate

import kotlin.properties.Delegates
import kotlin.properties.ObservableProperty
import kotlin.reflect.KProperty

class M {
    val s: String by MyDelegate { "Hello" }
    var a by Delegates.notNull<String>()
    var b by Delegates.observable("b") { _: KProperty<*>, oldValue: String, newValue: String ->
        println("'$oldValue' has been changed by '$newValue'!")
    }
    var c by Delegates.vetoable("world") { _: KProperty<*>, oldValue: String, newValue: String ->
        oldValue.length < newValue.length
    }
    val d by lazy { "d" }
    var e by object : ObservableProperty<String>("hhhh") {

        override fun beforeChange(property: KProperty<*>, oldValue: String, newValue: String): Boolean {
            val flag = oldValue.length < newValue.length
            println("'$newValue'长度${if (flag) "大" else "小"}于'$oldValue', 修改${if (flag) "成功" else "失败"}!")
            return flag // 为false则允许修改
        }

        override fun afterChange(property: KProperty<*>, oldValue: String, newValue: String) {
            println("'$oldValue' 被修改为 '$newValue'!")
        }
    }
}

class MyDelegate<T>(val init: () -> T) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return init()
    }
}

fun main() {
    val m = M()
    println(m.s)
//    println(m.a)
    println(m.b)
    println(m.c)

    m.e = "hh"
    m.e = "hhhhhh"

}

***分割线****


注意到getValue和setValue竟然是两个operator方法,那么它们是实现了哪个操作符呢?没错,就是等号=,怪不得kotlin不让我重载这个操作符,原来它被用在属性代理里了。
目前kotlin还不支持直接点进去查看等号的具体实现,也就没有其他操作符那么方便的看源码了。不过想看的话,点到对应的属性,找熟悉的by关键字,然后看看by关键字后面的代理对象的实现就ok啦。

相关文章

网友评论

      本文标题:kotlin提供的一些属性代理

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