用一个属性代理另一个对象的属性

最近好忙呀,上周因为上上周末加班都没顾上写文章。 好在昨天活动见到了膜拜已久的冰冰,很好很强大。

今天来分享给大家一个属性代理的例子。总是有人问我属性代理有什么用,这个也许可以为你提供些思路。

话说我们经常会有这样的需求场景:

 
 
  1. class Wrapper {

  2.    private val wrapped: Wrapped = Wrapped(false)

  3.    var x : Boolean

  4.        get() = wrapped.x

  5.        set(value){

  6.            wrapped.x = value

  7.        }

  8.    var y: Int = 0

  9.        set(value){

  10.            wrapped.setY(value)

  11.            field = value

  12.        }

  13.    val z: Long

  14.        get() = wrapped.z

  15. }

我们用一个类的属性来代理内部对象的属性,这样做的目的当然是希望内部的 target 不被暴露,同时部分的 api 也可以让外部获得访问权限。

可是这些代码看起来总觉得,应该可以更简单一些,比如用个属性代理什么的。

 
 
  1. class ObjectPropertyDelegate<T, R>(val target: R, val getter: ((R) -> T)? = null, val setter: ((R, T) -> Unit)? = null, defaultValue: T? = null) {

  2.    private var value: T? = defaultValue

  3.    operator fun getValue(ref: Any, property: KProperty<*>): T {

  4.        return getter?.invoke(target)?:value!!

  5.    }

  6.    operator fun setValue(ref: Any, property: KProperty<*>, value: T) {

  7.        setter?.invoke(target, value)

  8.        this.value = value

  9.    }

  10. }

当然这个 ObjectPropertyDelegate 使用的是没有绑定 receiver 的 getter 和 setter,所以我们在使用时就可以把文章开头的代码改写为:

 
 
  1. class Wrapper {

  2.    private val wrapped: Wrapped = Wrapped(false)

  3.    var x by ObjectPropertyDelegate(wrapped, Wrapped::x, Wrapped::x::set) // getter 处也可使用 Wrapped::x::get

  4.    var y by ObjectPropertyDelegate(wrapped, setter = Wrapped::setY, defaultValue = 0)

  5.    val z by ObjectPropertyDelegate(wrapped, Wrapped::z) // getter 处也可使用 Wrapped::z::get

  6. }

对于 x,似乎我们还可以简化一下,毕竟它是一个属性,通过一个属性我们似乎就可以把它的 setter 直接拿到,而不需要显式的传入了。

我们为我们的 ObjectPropertyDelegate 添加一个副构造器如下:

 
 
  1. ...

  2.    constructor(target: R, property: KProperty1<R, T>, defaultValue: T? = null)

  3.    :this(target, property, if(property is KMutableProperty1<*, *>) (property as KMutableProperty1<R, T>)::set else null, defaultValue)

  4. ...

那么我们的 Wrapper 就可以进一步简化:

 
 
  1. class Wrapper {

  2.    private val wrapped: Wrapped = Wrapped(false)

  3.    var x by ObjectPropertyDelegate(wrapped, Wrapped::x)

  4.    var y by ObjectPropertyDelegate(wrapped, setter = Wrapped::setY, defaultValue = 0)

  5.    val z by ObjectPropertyDelegate(wrapped, Wrapped::z)

  6. }

但你以为这就是巅峰状态了吗?Naive。既然是 ObjectDelegate,我们不免就要想,为什么不能用绑定了 receiver 的属性或者函数引用作为参数呢?

于是乎,我们定义了另外一个 ObjectPropertyDelegate0 的代理类:

 
 
  1. class ObjectPropertyDelegate0<T>(val getter: (() -> T)? = null, val setter: ((T) -> Unit)? = null, defaultValue: T? = null) {

  2.    constructor(propertyRef: PropertyReference, defaultValue: T? = null)

  3.            :this((propertyRef as KProperty0<T>)::get, if(propertyRef is KMutableProperty0<*>) (propertyRef as KMutableProperty0<T>)::set else null, defaultValue)

  4.    private var value: T? = defaultValue

  5.    operator fun getValue(ref: Any, property: KProperty<*>): T {

  6.        return getter?.invoke()?:value!!

  7.    }

  8.    operator fun setValue(ref: Any, property: KProperty<*>, value: T) {

  9.        setter?.invoke(value)

  10.        this.value = value

  11.    }

  12. }

那么我们进一步简化 Wrapper 的代码:

 
 
  1. class Wrapper {

  2.    private val wrapped: Wrapped = Wrapped(false)

  3.    var x by ObjectPropertyDelegate0(wrapped::x)

  4.    var y by ObjectPropertyDelegate0(setter = wrapped::setY, defaultValue = 0)

  5.    val z by ObjectPropertyDelegate0(wrapped::z)

  6. }

简单说下,这个 ObjectPropertyDelegate0 的主构造器仍然是留给直接传入函数引用的情况,例如 wrapped::setY,副构造器则用于支持直接传入属性,例如 wrapped::x

如果你对 Kotlin 的属性或者函数引用类型的命名比较熟悉的话,你应该知道前面的 ObjectPropertyDelegate 这时候就应该被命名为 ObjectPropertyDelegate1 了。

你以为这就完了吗?怎么会。前面的代码看上去还是不够直接,不够简洁,不如我们为属性和函数定义一个扩展吧:

 
 
  1. fun <T> KProperty0<T>.delegator(defaultValue: T? = null) = ObjectPropertyDelegate0(propertyRef = this as PropertyReference, defaultValue = defaultValue)

  2. fun <T, R> KProperty1<R, T>.delegator(receiver: R, defaultValue: T? = null) = ObjectPropertyDelegate1(receiver, property = this, defaultValue = defaultValue)

  3. fun <T> KFunction1<T, Unit>.delegator(defaultValue: T? = null) = ObjectPropertyDelegate0(setter = this, defaultValue = defaultValue)

  4. fun <T, R> KFunction2<R, T, Unit>.delegator(receiver: R, defaultValue: T? = null) = ObjectPropertyDelegate1(receiver,setter = this, defaultValue = defaultValue)

于是乎,最终的版本就是这样:

 
 
  1. class Wrapper {

  2.    private val wrapped: Wrapped = Wrapped(false)

  3.    var x by wrapped::x.delegator()

  4.    var y by wrapped::setY.delegator(defaultValue = 0)

  5.    val z by wrapped::z.delegator()

  6. }


今天的例子基本上到这儿接近尾声了,不过我再提一句,这个例子需要引入的包是这样的:

 
 
  1. import kotlin.jvm.internal.PropertyReference

  2. import kotlin.reflect.*

如果对于反射以及属性及函数引用这样的概念和知识点不是很了解,那么这篇文章可能会看起来比较迷。。

本文所涉及的代码已经托管在 GitHub:https://github.com/enbandari/ObjectPropertyDelegate,并发布在 jcenter 上。

大家如果有兴趣,也可以在 gradle 中引入:

 
 
  1. compile 'com.bennyhuo.kotlin:opd:1.0-rc'

最后再说一句,估计雀雀又要吐槽我了,这也是没有办法的事儿,哈哈。就这样。


转载请注明出处:微信公众号 Kotlin

640?wx_fmt=jpeg


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值