kotlin代理模式就是這麼簡單(委託)

weixin_34232744發表於2018-07-13

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的流程就是這樣。

相關文章