Kotlin學習:委託的理解

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的官方文檔進行學習吧。

總結:委託其實就是通過委託的對象,實現對原代理對象的一些方法或者屬性的替換,令其按照委託的對象的方法來執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章