Kotlin中的委托

41 阅读3分钟

委托,也就是委托模式,又叫代理模式,

定义:它是为其他对象提供一种代理以控制对这个对象的访问。

使用场景:当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。

1 委托的具体场景:

小明以前在公司上班时,就遇到过被老板拖欠工资甚至克扣工资的情况,这种情况下小明还是通过法律途径来解决问题,一旦小明选择了走法律途径解决该纠纷,那么不可避免地就需要请一个律师来作为自己的诉讼代理人,我们将诉讼的流程抽象在一个接口类中。

诉讼接口类:

interface ILawSuit {
    //提交申请
    fun submit()

    //进行举证
    fun burden()

    //开始辩护
    fun defend()

    //诉讼完成
    fun finish()
}

代理律师类:

class Lawyer(val name:String) :ILawSuit {
    override fun submit() {
        Log.d("tanyonglin", "$name 帮你在提交申请,---老板拖欠工资,申请仲裁")
    }

    override fun burden() {
        Log.d("tanyonglin", "$name 帮你在进行举证,---这是合同和过去一年的银行工资流水!")
    }

    override fun defend() {
        Log.d("tanyonglin", "$name 帮你在开始辩护,---正确确凿,不需要再说什么了")
    }

    override fun finish() {
        Log.d("tanyonglin", "$name 帮你诉讼完成,---诉讼成功")
    }
}

具体诉讼人:

class XiaoMing(private val iLawSuit: ILawSuit) :ILawSuit by iLawSuit {
}

在kotlin中,委托用关键字by修饰,by后面就是你委托的对象,可以是一个表达式,在本例中,通过by iLawSuit委托给了具体的被委托对象。

image.png

2 kotlin实现属性委托的方式

kotlin标准库中声明了2个含所需operator方法的ReadOnlyProperty/ReadWriteProperty接口:

public fun interface ReadOnlyProperty<in T, out V> {
    /**
     * 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: T, property: KProperty<*>): V
}

/**
 * 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 T the type of object which owns the delegated property.
 * @param V the type of the property value.
 */
public interface ReadWriteProperty<in T, V> : ReadOnlyProperty<T, V> {
    /**
     * 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 override operator fun getValue(thisRef: T, property: KProperty<*>): V

    /**
     * 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: T, property: KProperty<*>, value: V)
}

被委托类实现这两个接口其中之一就可以了。

// val 属性委托实现
class Delegate1: ReadOnlyProperty<Any, String> {
    override fun getValue(thisRef: Any, property: KProperty<*>): String {
        return "通过实现ReadOnlyProperty实现,name:${property.name}"
    }
}
// var 属性委托实现
class Delegate2: ReadWriteProperty<Any, Int> {
    override fun getValue(thisRef: Any, property: KProperty<*>): Int {
        return  20
    }
    override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {
        Log.d("tanyonglin", "委托属性为: ${property.name} 委托值为: $value")
    }
}
// 属性委托
val d1: String by Delegate1()
var d2: Int by Delegate2()

测试代码如下: image.png

3 Kotlin标准库中提供了几个委托

延迟属性(lazy properties):其值只在首次访问时计算 可观察属性(observable properties):监听器会收到有关此属性变更的通知

3.1 延迟属性lazy

lazy()是接受一个lambda并返回Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托:第一次调用get()会执行已传递给lazy()的lambda表达式并记录结果,后续调用get()只是返回记录的结果。

val lazyProp: String by lazy {
    Log.d("tanyonglin", "lazyProp Hello,第一次调用才会执行我")
    "今天520节日快乐"
}
Log.d("tanyonglin", lazyProp)
Log.d("tanyonglin", lazyProp)
Log.d("tanyonglin", lazyProp)

image.png

3.2 lazy也可以接受参数,提供了以下三个参数: public enum class LazyThreadSafetyMode {

/**
 * Locks are used to ensure that only a single thread can initialize the [Lazy] instance.
 */
SYNCHRONIZED,

/**
 * Initializer function can be called several times on concurrent access to uninitialized [Lazy] instance value,
 * but only the first returned value will be used as the value of [Lazy] instance.
 */
PUBLICATION,

/**
 * No locks are used to synchronize an access to the [Lazy] instance value; if the instance is accessed from multiple threads, its behavior is undefined.
 *
 * This mode should not be used unless the [Lazy] instance is guaranteed never to be initialized from more than one thread.
 */
NONE,

} 三个参数解释如下:

LazyThreadSafetyMode.SYNCHRONIZED:添加同步锁,使lazy延迟初始化线程安全

LazyThreadSafetyMode.PUBLICATION:初始化的lambda表达式可以在同一时间被多次调用,但是只有第一个返回值作为初始化的值。

LazyThreadSafetyMode.NONE:没有同步锁,多线程访问时候,非线程安全的。

val lazyProp: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
Log.d("tanyonglin", "lazyProp Hello,第一次调用才会执行我")
"今天520节日快乐"

} 这里待会还分析一下源码单独:

4.1 可观察属性Observable

如果你要观察一个属性的变化过程,那么就可以将属性委托给Delegates.observable,observable函数原型如下: /**

  • Returns a property delegate for a read/write property that calls a specified callback function when changed.

  • @param initialValue the initial value of the property.

  • @param onChange 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.

  • @sample samples.properties.Delegates.observableDelegate / public inline fun observable(initialValue: T, crossinline onChange: (property: KProperty<>, oldValue: T, newValue: T) -> Unit): ReadWriteProperty<Any?, T> = object : ObservableProperty(initialValue) { override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue) }

    接受2个参数: initialValue:初始值 onChange:属性值被修改时的回调处理器,回调有三个参数property,oldValue,newValue,分别为:被赋值的属性,旧值与新值。

    使用如下:

var observableProp: String by Delegates.observable("默认值:刚出生") { property, oldValue, newValue ->
 Log.d("tanyonglin", "property: $property: $oldValue -> $newValue ")
}

image.png

可以看到,每一次赋值,都能观察到值的变化过程。

4.2 vetoable函数

vetoable与observable一样,可以观察属性值的变化,不同的是,vetoable可以通过处理器函数来决定属性值是否生效。

来看这样一个例子:声明一个Int类型的属性vetobleProp,如果新的值比旧的值大,则生效,否则不生效。

var observableProp1: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
   newValue > oldValue
}

image.png

可以看到10->5的赋值是没有生效的。