Kotlin by 關(guān)鍵字解讀(委托)

by 就是Kotlin 幫我們實現(xiàn)代理模式的捷徑。
by可以實現(xiàn)兩種代理,一種是接口代理,一種是屬性代理。

首先看接口代理

接口代理和我們在java中使用的代理是一個東西,即在不改變原對象的情況下通過代理對象對原對象進行一次封裝來添加一些控制。

在Java中實現(xiàn)靜態(tài)代理通常要這樣做:

interface IProxy{
    fun doSomething()
}

class ProxyImpl(private val proxy : IProxy) : IProxy {
    override fun doSomething() {
        println("代理前")
        proxy.doSomething()
        println("代理后")
    }

}

使用kotlin 的by關(guān)鍵字,可以這樣實現(xiàn)代理

class ByProxyImpl(private val proxy : IProxy) : IProxy by proxy

這就代表通過ByProxyImpl實例調(diào)用doSomething 方法會全部委托給 傳入的proxy參數(shù)。如果想要在委托的方法中添加一些額外操作,同樣可以覆蓋該方法。

class ByProxyImpl(private val proxy : IProxy) : IProxy by proxy {
    override fun doSomething() {
        println("代理前")
        proxy.doSomething()
        println("代理后")
    }
}

那這樣和Java方式的代理模式就沒有區(qū)別了,完全看不出優(yōu)點。
上面說的只有一個方法,那如果有多個方法呢?

普通的代理模式

class ProxyImpl(private val proxy : IProxy) : IProxy {
    override fun doSomething() {
        println("代理前")
        proxy.doSomething()
        println("代理后")
    }

    override fun doSomething1() {
        proxy.doSomething1()
    }

    override fun doSomething2() {
        proxy.doSomething2()
    }

}

盡管在doSomething1 和doSomething2 中什么都沒有做,但是為了保證ProxyImpl功能完整,必須得覆蓋方法并調(diào)用代理方法。

使用by關(guān)鍵字

class ByProxyImpl(private val proxy : IProxy) : IProxy by proxy {
    override fun doSomething() {
        println("代理前")
        proxy.doSomething()
        println("代理后")
    }
}

無論有多少個方法,只需要覆蓋需要代理的方法就行了,這點還是比較實用的。

屬性代理

大家對屬性代理可能比較陌生,因為在Java中沒有這個東西,其實從宏觀上講屬性代理和接口代理是一個意思。

屬性代理實現(xiàn)

class PropertyProxy<T> (var value : T){
    operator fun getValue(t: Any?, property: KProperty<*>): T {
        return value
    }
    operator fun <T> setValue(t: Any?, property: KProperty<*>, t1: T) {

    }
}

 // 使用屬性代理

var value : String by PropertyProxy("我是代理")
value = "1"
println(value)

這里插一句題外話,在學(xué)習(xí)Compose的時候,一個地方讓我非常迷惑

val items: List<TodoItem> by todoViewModel.todoItems.observeAsState(listOf())

我就想observeAsState 明明返回的是一個State,怎么通過一個by就變成list了呢?后來才知道這是Kotlin的委托,感覺很神奇。

其實屬性代理 無非就是對屬性的set和get方法代理。

 val value : String by PropertyProxy("我是代理")

// 轉(zhuǎn)成java
PropertyProxy var10000 = new PropertyProxy("我是代理");
KProperty var2 = $$delegatedProperties[0];
PropertyProxy value = var10000;
value.setValue((Object)null, var2, "1");
Object var3 = value.getValue((Object)null, var2);
boolean var4 = false;
System.out.println(var3);

可以看到 對value的寫和讀 完全調(diào)用了代理類的set和get方法。

大家最早接觸到的屬性代理 應(yīng)該是Kotlin提供的懶加載

 val lazyValue by lazy { "" }

lazy 可以做到 在get的時候才調(diào)用傳進去的lambda 對 對象進行初始化

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
                }
            }
        }
}

但是 在Lazy中沒看到有g(shù)etValue方法啊,找來找去發(fā)現(xiàn)是通過擴展方法來添加的

@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value

幸虧和Lazy類在一個文件里,要不找破頭也找不到,所以我們寫擴展函數(shù)的時候也要注意,不要隨意擺放。

到此 Kotlin by 關(guān)鍵字算是了解的差不多了,如果再了解到有其他用途,后續(xù)補充。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容