kotlin代理模式就是這麼簡單
我們都熟悉代理模式,就是把自己要做的事情委託給另外一個物件,這個物件就代理物件,java的代理模式包括靜態代理以及動態代理,在這裡就不過多贅述了,今天我們來說一說kotlin的代理。
kotlin中可以零樣板程式碼地原生支援它,kotlin中包括,類代理,以及屬性代理
類代理
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 輸出 10
}
複製程式碼
可以看到kotlin實現類代理非常簡單,通過關鍵字by就可以讓b代理自己的方法,b將會在Derived中內部儲存,並且編譯器將生成轉發給b的所有 Base的方法。
屬性代理
談到屬性代理,我們用到的較多的有lazy、Delegates.notNull()、Delegates.observable()等,這些都是kotlin自帶的一些標準代理,那我們自己來寫一個屬性代理
class Example {
val p: String by Delegate()
}
class Delegate {
}
複製程式碼
如果我們直接這樣寫的話,會發現Delegate下面會有錯誤提示, miss 'getValue(mainActivity: MainActivity, property: KProperty<*>): Any' method on delegate of type 'Delegate' 這個提示的意思是說需要提供一個getValue的方法,如果我們把p前面改成var,那麼編譯器又會提示一個需要setValue方法。
class Example {
var p: String by Delegate()
}
class Delegate {
private var mRealValue = ""
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return mRealValue
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
mRealValue = value
}
}
複製程式碼
其實屬性的代理,就是把自己的屬性對應的get()和set()會被代理給它的getValue()和setValue()方法。兩函式都需要用 operator 關鍵字來進行標記,代理類可以實現包含所需 operator 方法的 ReadOnlyProperty 或 ReadWriteProperty 介面之一,這倆介面是在 Kotlin 標準庫中宣告的。
在每個代理屬性的實現的背後,Kotlin 編譯器都會生成輔助屬性並代理給它。例如,對於屬性 prop,生成隱藏屬性 prop$delegate,而訪問器的程式碼只是簡單地代理給這個附加屬性:
class C {
var prop: Type by MyDelegate()
}
// 這段是由編譯器生成的相應程式碼:
class C {
private val prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
複製程式碼
Kotlin 編譯器在引數中提供了關於 prop 的所有必要資訊:第一個引數 this 引用到外部類C的例項而this::prop是KProperty型別的反射象,該物件描述prop自身。 我們繼續分析一下lazy方法,自己寫一個lazy的屬性
class Demo1 {
private val a by lazy { 1 }
}
複製程式碼
使用kotlin bytecode工具檢視位元組碼,再進行反編譯
public final class Demo1 {
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(Demo1.class), "a", "getA()I"))};
private final Lazy a$delegate;
private final int getA() {
Lazy var1 = this.a$delegate;
KProperty var3 = $$delegatedProperties[0];
return ((Number)var1.getValue()).intValue();
}
public Demo1() {
this.a$delegate = LazyKt.lazy((Function0)null.INSTANCE);
}
}
複製程式碼
編譯器為我們生成了一個a$delegate的Lazy,以及$$delegatedProperties的KProperty[]物件,還有一個getA的方法,並且在構造裡面通過 LazyKt.lazy((Function0)null.INSTANCE)初始化了a$delegate,這個方法其實就是我們的lazy()方法,我們繼續看lazy方法
public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
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)
}
複製程式碼
在 SynchronizedLazyImpl 實現程式碼裡,通過 _value 用來真正儲存屬性的值。_value 的預設值是 UNINITIALIZED_VALUE (一個自定義的物件)。當 _value 不是預設值的時候,就會直接把 _value 的值作為 getValue() 的返回;當 _value 還是預設值的時候,就會呼叫 initializer 初始化表示式完成初始化,賦值給 _value 並作為 getValue() 的返回
所以我們再獲取a的值時會去先呼叫getA()方法,回去呼叫Lazy的getValue方法,然後就會走SynchronizedLazyImpl的懶屬性的邏輯,整個lazy的流程就是這樣。