Kotlin的委托模式看了官方的还是有一些迷惑,决定写一篇博文记录一下。
委托基础
首先我们要了解委托模式到底是什么:
在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理
用wiki中的一个简单的例子来说明:
class RealPrinter { // the "delegate"
void print() {
System.out.print("something");
}
}
class Printer { // the "delegator"
RealPrinter p = new RealPrinter(); // create the delegate
void print() {
p.print(); // delegation
}
}
public class Main {
// to the outside world it looks like Printer actually prints.
public static void main(String[] args) {
Printer printer = new Printer();
printer.print();
}
}
在创建的Printer中我们再创建了一个真实的RealPrinter来执行Printer的打印操作。
现在我们看回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() {
val b = BaseImpl(10)
Derived(b).print()
}
这里Derived中的b是一个委托的对象,即上文中的RealPrinter,由这个类来执行真正的打印方法。
在这个例子的基础上我们可以做一下扩展
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(val b: Base) : Base by b{
override fun print() { b.print() }
}
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
这其实就是类似java中动态代理的实现,而代理的类就是Derived,它使用了被代理的类中的print方法来执行,我们可以围绕这个print方法增强,类似Spring中的AOP
这里动态代理和委托的区别是,动态代理可以覆写接口中的方法,而委托是不可以覆写接口方法的(相当于用委托类的方法进行覆写),因此不能在原print方法的基础上进行增强,而只能新增一个方法,例如printNew来实现增强
委托属性
先贴官方示例:
import kotlin.reflect.KProperty
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
class Example {
var p: String by Delegate()
}
这里做一下解释。首先,这里的
operator fun getValue/setValue
其实是对应的var p 的get/set方法,也就是用代理对象的get/set替换了p的get/set方法
我们用一个方法测试一下
fun main() {
val example = Example()
println(example.p)
example.p = "1"
}
/* 结果如下
Example@327471b5, thank you for delegating 'p' to me!
1 has been assigned to 'p' in Example@327471b5.
我们发现,调用了代理对象的get/set方法
标准委托
同时,Kotlin官方还提供了几个标准的委托方法
lazy()
lazy其实接收一个supplier类型的lambda函数,所以我们才可以直接才后面开块lazy{ … }
他会在第一次调用属性的get方法时被调用,并且它是基于同步锁的,当然可以通过改变参数mode来实现更改
observable()
接收两个参数,初始值和每次赋值时会调用的handler。
相当于属性在每次执行set的时候,observable都会被调用。这个也是观察者模式的一种典型应用
vetoable()
接收一个初始值和Predicate谓词(即一个返回Boolean的lambda),Predicate有三个参数,property, oldValue, newValue。具体看官方示例
var max: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
newValue > oldValue
}
println(max) // 0
max = 10
println(max) // 10
max = 5
println(max) // 10
这里property其实就是通过反射获取的属性。而oldValue和newValue该怎么用通过例子就一目了然了。
当Predicate返回true的时候set才会生效
其他的委托大家还是通过Kotlin的官方文档进行学习吧。
总结:委托其实就是通过委托的对象,实现对原代理对象的一些方法或者属性的替换,令其按照委托的对象的方法来执行。