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的官方文檔進行學習吧。
總結:委託其實就是通過委託的對象,實現對原代理對象的一些方法或者屬性的替換,令其按照委託的對象的方法來執行。