Kotlin委托

Kotlin委托

1.Kotlin中的委托有两种形式,一种是委托模式,一种是委托属性。

2.委托模式是设计模式中的一种,它是有两个对象参与处理同意请求,接受请求的对象将请求委托给另一个对象来处理。Kotlin语法对于这种模式提供了一个简易的写法。

委托模式的好处是可以使用聚合来代替继承。
看一下代码,委托模式的写法为:

// 一个接口
interface Base {
    fun print()
}
// 第一个子类
class BaseImple1(val x:Int):Base {
    override fun print() { print(x) }
}
// 第二个子类
class BaseImple2(val baseImple1:BaseImple1) {
    fun print() { baseImple1.print() }
}
// 第三个子类
class BaseImple3(val baseImple1:BaseImple1) :Base {
    override fun print() { baseImple1.print(x) }
}

第二个子类和第三个子类都是委托了第一个子类来进行函数的处理的。

Kotlin针对于第三个子类的委托模式可以简化写法为:

// 第三个子类
class BaseImple3(val baseImple1:BaseImple1) :Base by baseImple1

对于BaseImple3对象的调用函数等行为,委托转发给了baseImple1此对象。

注意事项:

  • 规定必须要使用接口,而不能使用抽象类。
  • by关键字,委托的只是接口类中的函数,非接口中定义函数还是调用此对象所定义的函数和属性,不能委托。

3.委托属性

  • 延迟属性
  • 可观察属性
  • 多个属性保存在一个map

3.1 语法为:val/var <属性名>: <类型> by <表达式>,委托属性需要提供getValue()setValue()方法

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

3.2 延迟属性
lazy()第一次调用会执行lambda表达式并且记录结果,后续调用只会返回结果记录。此函数是线程安全的,不需要在外部添加额外同步代码,也可以通过传入参数来指定,源码为:

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

三种加载模式:

  • SYNCHRONIZED,调用SynchronizedLazyImpl
  • PUBLICATION,调用SafePublicationLazyImpl
  • NONE,调用UnsafeLazyImpl

SynchronizedLazyImpl源码分析:

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 {
                    // 第一次会走到这里执行lambda表达式,加结果保存到_value中,第二次则直接返回_value值,而不再执行lambda表达式中的内容
                    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)
}

内部有一个属性保存lambda表达式所执行的值,再次调用则直接返回,无需执行lambda

SafePublicationLazyImpl源码分析:

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
            // 第一次执行initializerValue,不回为null,则对value的值进行更新,initializer设置为null;
            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"
        )
    }
}

它是在不需要加锁,并发执行的情况下,可以通过此类来获取值,同lazy类似,都是第一次的时候执行lambda表达式,然后再次执行就直接通过此类中的属性返回值即可,区别只是是否加锁,可以在多个线程中执行。

UnsafeLazyImpl源码:

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

这个只是简单的进行了一下判断,当前若有值,则返回,没有的话就执行lambda保存表达式的值,跟锁等无关,最好在单线程中使用,要不然会有同步问题。

4.可观察属性

Delegates.observable()需要两个参数,一个是此值的初始值,一个是此属性被修改时lambda的处理程序。

Delegates.vetoable()需要参数同上一致,只不过是lambda的处理时机不一致,上面的是在属性修改之后,这个实在属性修改之前。

5.属性存储在map映射中

可以使用map来对类的属性保存,官网上的例子:

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

val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))

6.实际使用中的一些写法:

标准委托要自己写的话有ReadOnlyPropertyReadWriteProperty两个接口,也提供了Delegates类中的函数可以来进行委托,如果需要仿照此类写即可。
kotlinapplicationcontext的获取方式可以这么写:

class App : Application() {
    
    var instance by Delegates.notNull<Application>()
    
    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}

延迟加载基本使用lazy就可满足需求,下面是在网上看到的一个Androidsp的使用方法,觉得不错,贴一下代码:

class Preference<T>(private val context: Context, private val name: String,
        private val default: T) {

    private val prefs: SharedPreferences by lazy {
        context.getSharedPreferences("default", Context.MODE_PRIVATE)
    }

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T = findPreference(name, default)

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        putPreference(name, value)
    }

    @Suppress("UNCHECKED_CAST")
    private fun findPreference(name: String, default: T): T = with(prefs) {
        val res: Any = when (default) {
            is Long -> getLong(name, default)
            is String -> getString(name, default)
            is Int -> getInt(name, default)
            is Boolean -> getBoolean(name, default)
            is Float -> getFloat(name, default)
            else -> throw IllegalArgumentException("This type can be saved into Preferences")
        }

        res as T
    }

    @SuppressLint("CommitPrefEdits")
    private fun putPreference(name: String, value: T) = with(prefs.edit()) {
        when (value) {
            is Long -> putLong(name, value)
            is String -> putString(name, value)
            is Int -> putInt(name, value)
            is Boolean -> putBoolean(name, value)
            is Float -> putFloat(name, value)
            else -> throw IllegalArgumentException("This type can't be saved into Preferences")
        }.apply()
    }
}
// 单例调用此函数
object DelegatesExt {
    fun <T> preference(context: Context, name: String,
            default: T) = Preference(context, name, default)

}
// 具体使用
private var zipCode: Long by DelegatesExt.preference(this, ZIP_CODE, DEFAULT_ZIP)

这个写法,就是使用了委托属性,然后操作属性即可,而不需要再像原来一样写一大堆的样板代码了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,267评论 9 118
  • 本文是在学习和使用kotlin时的一些总结与体会,一些代码示例来自于网络或Kotlin官方文档,持续更新... 对...
    竹尘居士阅读 3,321评论 0 8
  • 面向对象编程(OOP) 在前面的章节中,我们学习了Kotlin的语言基础知识、类型系统、集合类以及泛型相关的知识。...
    Tenderness4阅读 4,479评论 1 6
  • 写在开头:本人打算开始写一个Kotlin系列的教程,一是使自己记忆和理解的更加深刻,二是可以分享给同样想学习Kot...
    胡奚冰阅读 1,277评论 0 6
  • 上午先生带孩子出去玩,我在家里没去。两个小时之后回来了,我在屋里听到声音没等敲门就把门打开了,宝宝一下子看到我了特...
    利萍阅读 141评论 0 0