Kotlin by属性委托
轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/119939781
本文出自【趙彥軍的博客】
文章目錄
- 屬性委托要求
- 委托原理
- 實戰演練,SharedPreference 委托
- 升級之旅 ReadWriteProperty
- 延遲委托 Lazy
在 Kotlin 中,通過 by 實現屬性委托,屬性委托 是什么意思呢?
簡單來說,就是屬性的 set、get 的操作,交給另一個對象器完成。
舉個例子:
class Example {var p: String by Delegate() }語法是: val/var <屬性名>: <類型> by <表達式>。在 by 后面的表達式是該 委托, 因為屬性對應的 get()(與 set())會被委托給它的 getValue() 與 setValue() 方法。 屬性的委托不必實現任何的接口,但是需要提供一個 getValue() 函數(與 setValue()——對于 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.")} }當我們從委托到一個 Delegate 實例的 p 讀取時,將調用 Delegate 中的 getValue() 函數, 所以它第一個參數是讀出 p 的對象、第二個參數保存了對 p 自身的描述 (例如你可以取它的名字)。 例如:
val e = Example() println(e.p)輸出結果:
Example@33a17727, thank you for delegating ‘p’ to me!
類似地,當我們給 p 賦值時,將調用 setValue() 函數。前兩個參數相同,第三個參數保存將要被賦予的值:
e.p = "NEW"輸出結果:
NEW has been assigned to ‘p’ in Example@33a17727.
屬性委托要求
對于一個只讀屬性(即 val 聲明的),委托必須提供一個操作符函數 getValue(),該函數具有以下參數:
- thisRef —— 必須與 屬性所有者 類型(對于擴展屬性——指被擴展的類型)相同或者是其超類型。
- property —— 必須是類型 KProperty<*> 或其超類型。
getValue() 必須返回與屬性相同的類型(或其子類型)。
class Resourceclass Owner {val valResource: Resource by ResourceDelegate() }class ResourceDelegate {operator fun getValue(thisRef: Owner, property: KProperty<*>): Resource {return Resource()} }對于一個可變屬性(即 var 聲明的),委托必須額外提供一個操作符函數 setValue(), 該函數具有以下參數:
- thisRef —— 必須與 屬性所有者 類型(對于擴展屬性——指被擴展的類型)相同或者是其超類型。
- property —— 必須是類型 KProperty<*> 或其超類型。
- value — 必須與屬性類型相同(或者是其超類型)。
getValue() 或/與 setValue() 函數可以通過委托類的成員函數提供或者由擴展函數提供。 當你需要委托屬性到原本未提供的這些函數的對象時后者會更便利。 兩函數都需要用 operator 關鍵字來進行標記。
委托原理
在每個委托屬性的實現的背后,Kotlin 編譯器都會生成輔助屬性并委托給它。 例如,對于屬性 prop,生成隱藏屬性 prop$delegate,而訪問器的代碼只是簡單地委托給這個附加屬性:
class C {var prop: Type by MyDelegate() }// 這段是由編譯器生成的相應代碼: 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) }簡單來說,委托之所以能實現,是因為kotlin 在編譯期間幫我們寫了代碼,動態的做了屬性的 set / get 方法
Kotlin 編譯器在參數中提供了關于 prop 的所有必要信息:第一個參數 this 引用到外部類 C 的實例而 this::prop 是 KProperty 類型的反射對象,該對象描述 prop 自身。
實戰演練,SharedPreference 委托
創建 UtilSharedPreference 委托類
/*** @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"} }到這里,我們已經實現了一個 SharedPreferences 方案了,運行了一下,非常完美。
在我查閱資料的時候,發現了一個大神的實現方式,地址是:
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/
下面貼一下代碼,寫的非常優秀。
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} }使用方式
// 該方式會存儲到SP中 SharedPreferencesUtils.User.name = "張無忌" SharedPreferencesUtils.User.phone = 18812345678 // 讀取 val name = SharedPreferencesUtils.User.name升級之旅 ReadWriteProperty
在寫委托的時候,要寫 getValue 、setValue 方法,也是有點麻煩,好在系統已經內置了接口。
自定義的委托類可以實現包含所需 operator 方法的 ReadOnlyProperty 或 ReadWriteProperty 接口之一。 這倆接口是在 Kotlin 標準庫中聲明的:
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) }如果我們要實現自己的委托就可以直接實現 ReadOnlyProperty 、ReadWriteProperty 接口就行了。
舉例如下:
/*** @author : zhaoyanjun* @time : 2021/8/25* @desc : 自定義代理類*/ 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并返回一個lazy實例,返回的實例可以作為實現延遲屬性的委托:第一次調用 get() 會執行已傳遞給 lazy() 的 lamda 表達式并記錄結果, 后續調用 get() 只是返回記錄的結果。
private val str: String by lazy {println("(=?ω?=)")"hello world" }// 多次輸出 str 變量,只會輸出一次(=?ω?=),多次 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) }總結
以上是生活随笔為你收集整理的Kotlin by属性委托的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Annotation注解
- 下一篇: Java Okio-更加高效易用的IO库