一、委托模式
概念:两个对象都能实现某种功能功能,当一个对象需要实现这个功能时,不是自己实现而是调用另一个对象实现。
委托模式不属于23种设计模式,但其代码实现和理念都类似于设计模式中的代理模式,可以大致把委托模式当成代理模式。
示例代码:
interface IBase {
fun doSomeThing()
}
class BaseDelegate : IBase {
override fun doSomeThing() {
Log.e("BaseDelegate" , "doSomeThing()方法委托给BaseDelegate处理")
}
}
class Base : IBase {
private val delegate = BaseDelegate()
override fun doSomeThing() {
delegate.doSomeThing()
}
}
二、kotlin中的委托
Kotlin 通过关键字 by 实现委托
2.1 类委托
类的即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。
我们修改一下上面Base类代码:
class Base (delegate :BaseDelegate): IBase by delegate
调用
val base = Base(BaseDelegate())
base.doSomeThing()
Base类和BaseDelegate类都实现了IBase接口,而Base类通过关键字by将接口实现委托给了一个BaseDelegate对象,当我们调用Base类的接口方法时,实际上调用的是BaseDelegate对象的接口方法,这就是委托。
我们在Android studio编译器中将上面的Base类转换为Java代码看一下:是不是跟我们在委托模式里的实现的代码几乎一样。
public final class Base implements IBase {
// $FF: synthetic field
private final BaseDelegate $$delegate_0;
public Base(@NotNull BaseDelegate delegate) {
Intrinsics.checkNotNullParameter(delegate, "delegate");
super();
this.$$delegate_0 = delegate;
}
public void doSomeThing() {
this.$$delegate_0.doSomeThing();
}
}
2.2 属性委托
属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理。属性委托的格式如下:
属性定义 + by + 委托类。
我们需要定义一个委托类,属性的 getter 以及setter将被委托给这个对象的 getValue() 和 setValue() 方法,getValue() 和 setValue() 方法格式固定:
operator fun getValue(thisRef : Any? , property : KProperty<*>) : T {
return T
}
operator fun setValue(thisRef : Any? , property : KProperty<*> , value : T) {
}
参数解析:
thisRef 表示属性所在的类,property 表示被委托的属性,value 表示 setter 需要设置给属性的值。T 这里表示属性的类型。
示例代码:
private var userName : String by UserNameDelegate()
class UserNameDelegate {
private var realUserName = "真实姓名"
operator fun getValue(thisRef : Any? , property : KProperty<*>) : String {
Log.e("UserNameDelegate" , "委托了属性获取")
return realUserName
}
operator fun setValue(thisRef : Any? , property : KProperty<*> , value : String) {
Log.e("UserNameDelegate" , "委托了属性设置")
realUserName = value
}
}
调用:
Log.e("userName" , userName)
userName = "李四"
Log.e("userName" , userName)
打印如下:
UserNameDelegate: 委托了属性获取
userName: 真实姓名
UserNameDelegate: 委托了属性设置
UserNameDelegate: 委托了属性获取
userName: 李四
这其实很好理解,我们前面已经知道属性的调用和赋值 其实是用get和set实现的,通过委托,get和set会调用委托对象的getValue和 setValue方法,可以使用编译器将上面的代码转换为Java,一看就清晰。
我们直接写getValue() 和 setValue() 方法会比较麻烦,所以委托类也可以实现接口ReadOnlyProperty(只读属性)、ReadWriteProperty(可读可写属性),这两个接口定义了getValue() 和 setValue() 方法,我们只需要实现就可以了,更加方便。
public fun interface ReadOnlyProperty<in T, out V> {
public operator fun getValue(thisRef: T, property: KProperty<*>): V
}
public interface ReadWriteProperty<in T, V> : ReadOnlyProperty<T, V> {
public override operator fun getValue(thisRef: T, property: KProperty<*>): V
public operator fun setValue(thisRef: T, property: KProperty<*>, value: V)
}
三、标准委托
kotlin内置了许多委托实现。
3.1 by lazy延迟属性
lazy() 是一个函数, 接受一个 Lambda 表达式作为参数, 返回结果一个 Lazy 对象 ,Lazy 就是属性委托的实现类。如下:该方法返回了一个SynchronizedLazyImpl对象
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
委托的话 Lazy 需要实现getValue()方法,这是通过扩展实现的,如下
@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
看一下SynchronizedLazyImpl类:继承自Lazy,重写了属性value,第一次属性调用 get() 会执行已传递给 lazy()方法 的 lamda 表达式并记录结果, 后续调用 get() 只是返回记录的结果,代码很简单,看一下了解即可。
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
}
3.2 Map映射
可以把一个类的属性委托给一个map中同名的键值对
示例代码:
class User(val map : Map<String , Any?>) {
val userName : String by map
val userAge : Int by map
}
val user = User(mapOf("userName" to "用户名:李四" , "userAge" to 25))
Log.e("用户信息" , user.userName)
Log.e("用户信息" , user.userAge.toString())
打印如下:
用户信息: 用户名:李四
用户信息: 25
我们在编译器中点击by关键字:可以看到跳转到了Map的一个getValue扩展方法。
@kotlin.internal.InlineOnly
public inline operator fun <V, V1 : V> Map<in String, @Exact V>.getValue(thisRef: Any?, property: KProperty<*>): V1 =
@Suppress("UNCHECKED_CAST") (getOrImplicitDefault(property.name) as V1)
internal fun <K, V> Map<K, V>.getOrImplicitDefault(key: K): V {
if (this is MapWithDefault)
return this.getOrImplicitDefault(key)
return getOrElseNullable(key, { throw NoSuchElementException("Key $key is missing in the map.") })
}
internal inline fun <K, V> Map<K, V>.getOrElseNullable(key: K, defaultValue: () -> V): V {
val value = get(key)
if (value == null && !containsKey(key)) {
return defaultValue()
} else {
@Suppress("UNCHECKED_CAST")
return value as V
}
}
我们追踪该方法,很明显,我们通过property.name获取属性名,然后用属性名作为key获取map里的value值.
上面代码里类的属性只能是只读的val,因为我们用的是Map只读,如果需要属性是var可读可写的,需要使用MutableMap,同样的并且可以想象的,MutableMap有getValue() 和 setValue() 的扩展方法,大家自己查看源码即可。
3.3 notNull
notNull 适用于那些无法在初始化阶段就确定属性值的场合。
示例代码:
var id : String by Delegates.notNull<String>()
查看源码:可以看到实现了ReadWriteProperty,并且重写了getValue方法如果是null抛出异常。
public fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = 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
}
}