Kotlin by属性委托
轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/zhaoyanjun6/article/details/119939781
本文出自【趙彥軍的博客】
文章目錄
- 屬性委托要求
- 委托原理
- 實(shí)戰(zhàn)演練,SharedPreference 委托
- 升級(jí)之旅 ReadWriteProperty
- 延遲委托 Lazy
在 Kotlin 中,通過(guò) by 實(shí)現(xiàn)屬性委托,屬性委托 是什么意思呢?
簡(jiǎn)單來(lái)說(shuō),就是屬性的 set、get 的操作,交給另一個(gè)對(duì)象器完成。
舉個(gè)例子:
class Example {var p: String by Delegate() }語(yǔ)法是: val/var <屬性名>: <類(lèi)型> by <表達(dá)式>。在 by 后面的表達(dá)式是該 委托, 因?yàn)閷傩詫?duì)應(yīng)的 get()(與 set())會(huì)被委托給它的 getValue() 與 setValue() 方法。 屬性的委托不必實(shí)現(xiàn)任何的接口,但是需要提供一個(gè) getValue() 函數(shù)(與 setValue()——對(duì)于 var 屬性)。 例如:
mport kotlin.reflect.KPropertyclass 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.")} }當(dāng)我們從委托到一個(gè) Delegate 實(shí)例的 p 讀取時(shí),將調(diào)用 Delegate 中的 getValue() 函數(shù), 所以它第一個(gè)參數(shù)是讀出 p 的對(duì)象、第二個(gè)參數(shù)保存了對(duì) p 自身的描述 (例如你可以取它的名字)。 例如:
val e = Example() println(e.p)輸出結(jié)果:
Example@33a17727, thank you for delegating ‘p’ to me!
類(lèi)似地,當(dāng)我們給 p 賦值時(shí),將調(diào)用 setValue() 函數(shù)。前兩個(gè)參數(shù)相同,第三個(gè)參數(shù)保存將要被賦予的值:
e.p = "NEW"輸出結(jié)果:
NEW has been assigned to ‘p’ in Example@33a17727.
屬性委托要求
對(duì)于一個(gè)只讀屬性(即 val 聲明的),委托必須提供一個(gè)操作符函數(shù) getValue(),該函數(shù)具有以下參數(shù):
- thisRef —— 必須與 屬性所有者 類(lèi)型(對(duì)于擴(kuò)展屬性——指被擴(kuò)展的類(lèi)型)相同或者是其超類(lèi)型。
- property —— 必須是類(lèi)型 KProperty<*> 或其超類(lèi)型。
getValue() 必須返回與屬性相同的類(lèi)型(或其子類(lèi)型)。
class Resourceclass Owner {val valResource: Resource by ResourceDelegate() }class ResourceDelegate {operator fun getValue(thisRef: Owner, property: KProperty<*>): Resource {return Resource()} }對(duì)于一個(gè)可變屬性(即 var 聲明的),委托必須額外提供一個(gè)操作符函數(shù) setValue(), 該函數(shù)具有以下參數(shù):
- thisRef —— 必須與 屬性所有者 類(lèi)型(對(duì)于擴(kuò)展屬性——指被擴(kuò)展的類(lèi)型)相同或者是其超類(lèi)型。
- property —— 必須是類(lèi)型 KProperty<*> 或其超類(lèi)型。
- value — 必須與屬性類(lèi)型相同(或者是其超類(lèi)型)。
getValue() 或/與 setValue() 函數(shù)可以通過(guò)委托類(lèi)的成員函數(shù)提供或者由擴(kuò)展函數(shù)提供。 當(dāng)你需要委托屬性到原本未提供的這些函數(shù)的對(duì)象時(shí)后者會(huì)更便利。 兩函數(shù)都需要用 operator 關(guān)鍵字來(lái)進(jìn)行標(biāo)記。
委托原理
在每個(gè)委托屬性的實(shí)現(xiàn)的背后,Kotlin 編譯器都會(huì)生成輔助屬性并委托給它。 例如,對(duì)于屬性 prop,生成隱藏屬性 prop$delegate,而訪問(wèn)器的代碼只是簡(jiǎn)單地委托給這個(gè)附加屬性:
class C {var prop: Type by MyDelegate() }// 這段是由編譯器生成的相應(yīng)代碼: class C {private val prop$delegate = MyDelegate()var prop: Typeget() = prop$delegate.getValue(this, this::prop)set(value: Type) = prop$delegate.setValue(this, this::prop, value) }簡(jiǎn)單來(lái)說(shuō),委托之所以能實(shí)現(xiàn),是因?yàn)閗otlin 在編譯期間幫我們寫(xiě)了代碼,動(dòng)態(tài)的做了屬性的 set / get 方法
Kotlin 編譯器在參數(shù)中提供了關(guān)于 prop 的所有必要信息:第一個(gè)參數(shù) this 引用到外部類(lèi) C 的實(shí)例而 this::prop 是 KProperty 類(lèi)型的反射對(duì)象,該對(duì)象描述 prop 自身。
實(shí)戰(zhàn)演練,SharedPreference 委托
創(chuàng)建 UtilSharedPreference 委托類(lèi)
/*** @author : zhaoyanjun* @time : 2021/8/25* @desc :*/ class UtilSharedPreference<T>(private val key: String, private val default: T) {operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Any) {sp.edit().apply {when (value) {is Long -> putLong(key, value)is String -> putString(key, value)is Int -> putInt(key, value)is Boolean -> putBoolean(key, value)is Float -> putFloat(key, value)is Set<*> -> putStringSet(key, value as Set<String>) // only support Set<String>else -> throw IllegalArgumentException("SharedPreferences can't be save this type")}.apply()}}operator fun getValue(thisRef: Any?, property: KProperty<*>): T {sp.apply {val res: Any = when (default) {is Long -> getLong(key, default)is String -> getString(key, default) ?: ""is Int -> getInt(key, default)is Boolean -> getBoolean(key, default)is Float -> getFloat(key, default)is Set<*> -> getStringSet(key, default as Set<String>) ?: default as Set<String>else -> throw IllegalArgumentException("SharedPreferences can't be get this type")}return res as T}}companion object {lateinit var sp: SharedPreferencesfun initSharedPreference(context: Context, fileName: String) {sp = context.getSharedPreferences(fileName, Context.MODE_PRIVATE)}} }使用如下:
class MainActivity : AppCompatActivity() {//委托var name: String by UtilSharedPreference("name", "")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)//初始化UtilSharedPreference.initSharedPreference(this, "sp_file")//取值val newName = name//賦值name = "ppp"} }到這里,我們已經(jīng)實(shí)現(xiàn)了一個(gè) SharedPreferences 方案了,運(yùn)行了一下,非常完美。
在我查閱資料的時(shí)候,發(fā)現(xiàn)了一個(gè)大神的實(shí)現(xiàn)方式,地址是:
https://wazing.github.io/2019/05/23/kotlin-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A7%94%E6%89%98%E6%96%B9%E5%BC%8F%E5%AE%9E%E7%8E%B0SharedPreferences/
下面貼一下代碼,寫(xiě)的非常優(yōu)秀。
object SharedPreferencesUtils {object User : Delegates() {override fun getSharedPreferencesName(): String = this.javaClass.simpleNamevar name by string()var phone by long()}abstract class Delegates {private val preferences: SharedPreferences by lazy {BaseApplication.instance.applicationContext.getSharedPreferences(getSharedPreferencesName(),Context.MODE_PRIVATE)}fun int(defaultValue: Int = 0) = object : ReadWriteProperty<Any, Int> {override fun getValue(thisRef: Any, property: KProperty<*>): Int {return preferences.getInt(property.name, defaultValue)}override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {preferences.edit().putInt(property.name, value).apply()}}fun string(defaultValue: String? = null) = object : ReadWriteProperty<Any, String?> {override fun getValue(thisRef: Any, property: KProperty<*>): String? {return preferences.getString(property.name, defaultValue)}override fun setValue(thisRef: Any, property: KProperty<*>, value: String?) {preferences.edit().putString(property.name, value).apply()}}fun long(defaultValue: Long = 0L) = object : ReadWriteProperty<Any, Long> {override fun getValue(thisRef: Any, property: KProperty<*>): Long {return preferences.getLong(property.name, defaultValue)}override fun setValue(thisRef: Any, property: KProperty<*>, value: Long) {preferences.edit().putLong(property.name, value).apply()}}fun boolean(defaultValue: Boolean = false) = object : ReadWriteProperty<Any, Boolean> {override fun getValue(thisRef: Any, property: KProperty<*>): Boolean {return preferences.getBoolean(property.name, defaultValue)}override fun setValue(thisRef: Any, property: KProperty<*>, value: Boolean) {preferences.edit().putBoolean(property.name, value).apply()}}fun float(defaultValue: Float = 0.0f) = object : ReadWriteProperty<Any, Float> {override fun getValue(thisRef: Any, property: KProperty<*>): Float {return preferences.getFloat(property.name, defaultValue)}override fun setValue(thisRef: Any, property: KProperty<*>, value: Float) {preferences.edit().putFloat(property.name, value).apply()}}fun setString(defaultValue: Set<String>? = null) = object :ReadWriteProperty<SharedPreferencesUtils, Set<String>?> {override fun getValue(thisRef: SharedPreferencesUtils, property: KProperty<*>): Set<String>? {return preferences.getStringSet(property.name, defaultValue)}override fun setValue(thisRef: SharedPreferencesUtils, property: KProperty<*>, value: Set<String>?) {preferences.edit().putStringSet(property.name, value).apply()}}fun clearAll() {preferences.edit().clear().apply()}abstract fun getSharedPreferencesName(): String} }使用方式
// 該方式會(huì)存儲(chǔ)到SP中 SharedPreferencesUtils.User.name = "張無(wú)忌" SharedPreferencesUtils.User.phone = 18812345678 // 讀取 val name = SharedPreferencesUtils.User.name升級(jí)之旅 ReadWriteProperty
在寫(xiě)委托的時(shí)候,要寫(xiě) getValue 、setValue 方法,也是有點(diǎn)麻煩,好在系統(tǒng)已經(jīng)內(nèi)置了接口。
自定義的委托類(lèi)可以實(shí)現(xiàn)包含所需 operator 方法的 ReadOnlyProperty 或 ReadWriteProperty 接口之一。 這倆接口是在 Kotlin 標(biāo)準(zhǔn)庫(kù)中聲明的:
ublic interface ReadOnlyProperty<in R, out T> {public operator fun getValue(thisRef: R, property: KProperty<*>): T }public interface ReadWriteProperty<in R, T> {public operator fun getValue(thisRef: R, property: KProperty<*>): Tpublic operator fun setValue(thisRef: R, property: KProperty<*>, value: T) }如果我們要實(shí)現(xiàn)自己的委托就可以直接實(shí)現(xiàn) ReadOnlyProperty 、ReadWriteProperty 接口就行了。
舉例如下:
/*** @author : zhaoyanjun* @time : 2021/8/25* @desc : 自定義代理類(lèi)*/ class MyUtil : ReadWriteProperty<Any, String> {override fun getValue(thisRef: Any, property: KProperty<*>): String {return ""}override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {} }延遲委托 Lazy
延遲委托是kotlin中最為常用的,lazy()后面接受lambda并返回一個(gè)lazy實(shí)例,返回的實(shí)例可以作為實(shí)現(xiàn)延遲屬性的委托:第一次調(diào)用 get() 會(huì)執(zhí)行已傳遞給 lazy() 的 lamda 表達(dá)式并記錄結(jié)果, 后續(xù)調(diào)用 get() 只是返回記錄的結(jié)果。
private val str: String by lazy {println("(=?ω?=)")"hello world" }// 多次輸出 str 變量,只會(huì)輸出一次(=?ω?=),多次 hello world原理如下
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 instanceprivate val lock = lock ?: thisoverride val value: Tget() {val _v1 = _valueif (_v1 !== UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST")return _v1 as T}return synchronized(lock) {val _v2 = _valueif (_v2 !== UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST") (_v2 as T)} else {val typedValue = initializer!!()_value = typedValueinitializer = nulltypedValue}}}override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUEoverride fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."private fun writeReplace(): Any = InitializedLazyImpl(value) }總結(jié)
以上是生活随笔為你收集整理的Kotlin by属性委托的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android Annotation注解
- 下一篇: AES加密 — 详解