Koltin委托属性

委托模式

在Kotlin中委托模式通过by关键字实现

interface Base {
    fun print()
}

class Derived(b: Base): Base by b

上述表达式表示b将代理Derived去实现interface Base的方法。

委托属性

延迟属性(Lazy)

Kotlin标准库中有很多有用的委托属性(delegates,代理属性),像使用lazy方法创建一个对象,只有在第一次使用的时候会被初始化。

lazy的重载方法

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

public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)

public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
    when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

由上可见,Lazy接口的实现类有三种SynchronizedLazyImpl,SafePublicationLazyImpl,UnsafeLazyImpl,前两者线程安全,但是实现方法有所不同,分别使用线程锁和原子类实现

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)
}

private class SafePublicationLazyImpl<out T>(initializer: () -> T) : Lazy<T>, Serializable {
    @Volatile private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    // this final field is required to enable safe publication of constructed instance
    private val final: Any = UNINITIALIZED_VALUE

    override val value: T
        get() {
            val value = _value
            if (value !== UNINITIALIZED_VALUE) {
                @Suppress("UNCHECKED_CAST")
                return value as T
            }

            val initializerValue = initializer
            // if we see null in initializer here, it means that the value is already set by another thread
            if (initializerValue != null) {
                val newValue = initializerValue()
                if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
                    initializer = null
                    return newValue
                }
            }
            @Suppress("UNCHECKED_CAST")
            return _value as T
        }

    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)

    companion object {
        private val valueUpdater = java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(
            SafePublicationLazyImpl::class.java,
            Any::class.java,
            "_value"
        )
    }
}

internal class UnsafeLazyImpl<out T>(initializer: () -> T) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    private var _value: Any? = UNINITIALIZED_VALUE

    override val value: T
        get() {
            if (_value === UNINITIALIZED_VALUE) {
                _value = initializer!!()
                initializer = null
            }
            @Suppress("UNCHECKED_CAST")
            return _value as T
        }

    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者的区别,官方文档上已有说明,如果该值只在一个线程中计算,并且所有线程会看到相同的值就使用SynchronizedLazyImpl;如果该值在多个线程可以同时执行,那么使用SafePublicationLazyImpl;如果初始化发生与使用在同一个线程就使用UnsafeLazyImpl,它不会有任何线程安全的保证和开销。

可观察属性(Observable)

在kotlin.properties.Delegates中,可以找到相关方法,分别是observable和vetoable,它们在被观察者的值发生改变时会执行回调,回调有三个值分别时被观察者的类型,旧值与新值;两者的区别在于,vetoable是否给被观察者赋值取决与回调函数的返回值。

var name: String by Delegates.observable("") { property, oldValue, newValue ->
}

var age: String by Delegates.vetoable("") { property, oldValue, newValue ->
    false
}

map代理(map delegate)

暂时没有使用过这种代理。

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int by map
}

按照官方文档的例子,来看传入键值对,map代理会根据键值给相对的属性赋值,相关的实现方法如下

@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)

@kotlin.jvm.JvmName("getOrImplicitDefaultNullable")
@PublishedApi
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.") })
}

如果传入的map没有实现MapWithDefault接口,key不存在时会抛出NoSuchElementException异常,因此初始化map时,需要做类似如下操作:

emptyMap<String, String>().withDefault { key -> "" }

这样找不到相对应的key-value时,就会使用默认值了,实现方法如下:

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
    }
}

使用的场景没有遇到过,但是有相关博客说,如果需要预定义keys的时候可以使用,相关博文会贴在下面

使用范例

Shared Preferences

private inline fun <T> SharedPreferences.delegate(
    defaultValue: T,
    key: String?,
    crossinline getter: SharedPreferences.(String, T) -> T,
    crossinline setter: Editor.(String, T) -> Editor
): ReadWriteProperty<Any, T> {
    return object: ReadWriteProperty<Any, T> {
        override fun getValue(thisRef: Any, property: KProperty<*>) =
            getter(key?:property.name, defaultValue)

        override fun setValue(thisRef: Any, property: KProperty<*>, value: T) =
            edit().setter(key?:property.name, value).apply()
    }
}

有了如上方法我们就可以改造SharedPreferences了:

// 存储Int
fun SharedPreferences.int(def: Int=0, key: String?=null) =
    delegate(def, key, SharedPreferences::getInt, Editor::putInt)

// 存储Long
fun SharedPreferences.long(def: Long=0L, key: String?=null) =
    delegate(def, key, SharedPreferences::getLong), Editor::putLong)

使用

var sthInt by prefs.int()

init {
    sthInt = 1
}

// sthInt的getter和setter都被代理了,取值时实际上调用的是SharedPreferences::getInt,被赋值的同时,也通过Editor::putInt将值存入了SharedPreferences中。

更多的使用案例会逐一的添加的此篇中。

参考

Kotlin delegates in Android development - Part1 by Fabio Collini

Kotlin 委托属性

评论