by 就是Kotlin 幫我們實現代理模式的捷徑。
by可以實現兩種代理,一種是接口代理,一種是屬性代理。
首先看接口代理
接口代理和我們在java中使用的代理是一個東西,即在不改變原對象的情況下通過代理對象對原對象進行一次封裝來添加一些控制。
在Java中實現靜態代理通常要這樣做:
interface IProxy{
fun doSomething()
}
class ProxyImpl(private val proxy : IProxy) : IProxy {
override fun doSomething() {
println("代理前")
proxy.doSomething()
println("代理后")
}
}
使用kotlin 的by關鍵字,可以這樣實現代理
class ByProxyImpl(private val proxy : IProxy) : IProxy by proxy
這就代表通過ByProxyImpl實例調用doSomething 方法會全部委托給 傳入的proxy參數。如果想要在委托的方法中添加一些額外操作,同樣可以覆蓋該方法。
class ByProxyImpl(private val proxy : IProxy) : IProxy by proxy {
override fun doSomething() {
println("代理前")
proxy.doSomething()
println("代理后")
}
}
那這樣和Java方式的代理模式就沒有區別了,完全看不出優點。
上面說的只有一個方法,那如果有多個方法呢?
普通的代理模式
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功能完整,必須得覆蓋方法并調用代理方法。
使用by關鍵字
class ByProxyImpl(private val proxy : IProxy) : IProxy by proxy {
override fun doSomething() {
println("代理前")
proxy.doSomething()
println("代理后")
}
}
無論有多少個方法,只需要覆蓋需要代理的方法就行了,這點還是比較實用的。
屬性代理
大家對屬性代理可能比較陌生,因為在Java中沒有這個東西,其實從宏觀上講屬性代理和接口代理是一個意思。
屬性代理實現
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)
這里插一句題外話,在學習Compose的時候,一個地方讓我非常迷惑
val items: List<TodoItem> by todoViewModel.todoItems.observeAsState(listOf())
我就想observeAsState 明明返回的是一個State,怎么通過一個by就變成list了呢?后來才知道這是Kotlin的委托,感覺很神奇。
其實屬性代理 無非就是對屬性的set和get方法代理。
val value : String by PropertyProxy("我是代理")
// 轉成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的寫和讀 完全調用了代理類的set和get方法。
大家最早接觸到的屬性代理 應該是Kotlin提供的懶加載
val lazyValue by lazy { "" }
lazy 可以做到 在get的時候才調用傳進去的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中沒看到有getValue方法啊,找來找去發現是通過擴展方法來添加的
@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
幸虧和Lazy類在一個文件里,要不找破頭也找不到,所以我們寫擴展函數的時候也要注意,不要隨意擺放。
到此 Kotlin by 關鍵字算是了解的差不多了,如果再了解到有其他用途,后續補充。