Kotlin:Delegate 委托属性

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010870167/article/details/82592720

在一些情况下,我们可能希望某些属性延迟加载,即在我们正在需要的时候才对它赋值;亦或者我们希望可以随时监听属性值的变化;又或者是多次调用该属性时,只对该属性赋值一次,既第一次赋值完就ok了,在上述这些场景中,代理属性就可以发挥作用了.

  • lazy properties
    它包含一个lambda,当第一次执行 getValue 的时候这个lambda会被调用,所以这个属性可以被延迟初始化。之后的调用都只会直接调用第一次返回的值;lazy 操作符是线程安全的
  • observable properties
    这个委托会帮我们监测我们希望观察的属性的变化.当被观察属性的 set 方法被调用的时候,它就会自动执行我们指定的lambda表达式。所以一旦该属性被赋了新的值,我们就会接收到被委托的属性、旧值和新值
  • storing properties
    属性的值会从一个map中获取value,属性的名字对应这个map中的key。

标准委托属性

Kotlin标准库为几种有用的委托提供了工厂方法

  • lazy
val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

fun main(args: Array<String>) {
    println(lazyValue)
    println(lazyValue)
}

// 输出:
computed!
Hello
Hello

调用了两次lazyValue,但是lambda:lazy{}只执行了一次,在第一执行的时候就把”Hello”赋值给lazyValue,当我们再次调用它的时候,他直接使用已存在的值.

例如:

class App : Application() {
    val database: SQLiteOpenHelper by lazy {
        MyDatabaseHelper(applicationContext)
    }
    override fun onCreate() {
        super.onCreate()
        val db = database.writableDatabase
    }
}

database并没有被真正初始化,直到第一次调用 onCreate 时。因为在那之后,我们才确保applicationContext存在,并且已经准备好可以被使用了

lazy 操作符是线程安全的,因为它里边的任务是同步执行的,所以在多线程环境中,第一个进入同步代码块执行出的结果,就是lazy-property的值

lazy还有其他加载方法:

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

LazyThreadSafetyMode:
1. SYNCHRONIZED 同步执行
2. 内部的计算过程可能被多次访问,但内部的属性是一个Volatile修饰的属性,所以在多线程环境中,被第一次访问获取数据后,此后的其它线程都共享该值。
3. 未对多线程环境,做任何处理。
所以在多线程环境中使用,会造成:计算和返回值都可能处在多个线程中。

注:lazy属性,只能声明为 val的,即它是只能get的

  • observable

使用方式属性 by Delegates.observable("init values") { lambda}
例:

interface TextChangedListener {
    fun onTextChanged(newText: String)
}

class PrintingTextChangedListener : TextChangedListener {
    override fun onTextChanged(newText: String) = println("Text is changed to: $newText")
}

class Data() {
    var listener: TextChangedListener? = null
    var text: String by Delegates.observable("init delegates") { property, oldValue, newValue ->
        listener?.onTextChanged(newValue)
    }
}

fun main(args: Array<String>) {
    val data = Data()
    data.listener = PrintingTextChangedListener()
    data.text = "lijunjie"
    data.text = "xxx"
}

// 输出:
Text is changed to: lijunjie
Text is changed to: xxx

property:属性(text)
oldValue:属性set之前的值
newValue:属性set之后的值

  • Storing Properties in a Map
    从Map中映射值到属性
    例:
class Data(map: Map<String, Any?>) {
    val userName: String by map
    val age: Int by map
    val avator: String by map

    override fun toString(): String {
        return "userName:$userName+age:$age+avator:$avator"
    }
}

val data = Data(mapOf(
        "userName" to "kotlin",
        "age" to 18,
        "avator" to "https://xxx.jpg"))

输出:
userName:kotlin+age:18+avator:https://xxx.jpg
  • 自定义的委托
object DelegatesExt {
    fun <T> notNullSingValue() = NotNullSingleValueVar<T>()
}

class NotNullSingleValueVar<T>: ReadWriteProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T =
            value ?: throw IllegalStateException("${property.name} not initialized")

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = if (this.value == null) value
        else throw IllegalStateException("${property.name} already initialized")
    }
    private var value: T? = null
}

在application中使用

companion object {
    var instance: BaseApplication by DelegatesExt.notNullSingValue()
}
override fun onCreate() {
    super.onCreate()
    instance = this
}

除此之外还有另外一种实现方式既使用opeartor,例:用来进行SharedPreference数据存储

class Preference<T>(private val name: String, private val default: T) {
    companion object {
        lateinit var preferences: SharedPreferences
        /**
         * init Context
         * @param context Context
         */
        fun setContext(context: Context) {
            preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
        }

        fun clear() {
            preferences.edit().clear().apply()
        }
    }

    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(preferences) {
        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(preferences.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()
    }
}

猜你喜欢

转载自blog.csdn.net/u010870167/article/details/82592720