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 关键字算是了解的差不多了,如果再了解到有其他用途,后续补充。