委託(Delegate)

1. 類的委託

委託模式 給實現繼承提供了很好的代替方式, Kotlin 在語法上支持這一點,所以並
不需要什麼樣板代碼。 Derived 類可以繼承 Base 接口並且指定一個對象代理
它全部的公共方法:

//  類的委託
//  AOP(面向方面編程)
interface Base
{
    fun print()
}
class BaseImpl(val x:Int):Base
{
    override fun print() {
        println(x)
    }
}
class Child1(b:Base):Base by b
{
   fun getName():String
   {
       return "Bill"
   }
}
class Child2(b:Base):Base by b
{

}
class Child3(b:Base):Base by b
{

}
fun main(args: Array<String>)
{
    val b = BaseImpl(10)
    Child1(b).print()
    Child2(b).print()
    Child3(b).print()
}

在 Child1的父類列表中的 by 從句會將 b 存儲在 Child1內部對象,並
且編譯器會生成 Base 的所有方法並轉給 b 。

2. 委託屬性

很多常用屬性,雖然我們可以在需要的時候手動實現它們,但更好的辦法是一次實
現多次使用,並放到庫。比如:

  • 延遲屬性:只在第一次訪問是計算它的值
  • 觀察屬性:監聽者從這獲取這個屬性

更新的通知 在 map 中存儲的屬性,而不是單獨存在分開的字段爲了滿足這些情形,Kotllin 支持代理屬性:

class Example {
var p: String by Delegate()
}

語法結構是: val/var : by 在 by 後
面的屬性就是代理,這樣這個屬性的 get() 和 set() 方法就代理給了它。
屬性代理不需要任何接口的實現,但必須要提供 get() 方法(如果是變量還需要
set() 方法)。像這樣:

class MyClass1
{
    //  將name屬性委託給Delegate類
    var name:String by Delegate()
}
class MyClass2
{
    var name:String by Delegate()
}
//  屬性委託類
class Delegate
{
    var name:String = ""
    operator fun getValue(thisRef:Any?, property:KProperty<*>):String
    {
        //  獲取thisRef指定的類名
        val className = thisRef.toString().substringBefore('@')
        println("getValue被調用")
        println("${className}.get已經被調用")
        return name
    }
    operator fun setValue(thisRef:Any?, property:KProperty<*>,value:String)
    {
        //  獲取thisRef指定的類名
        val className = thisRef.toString().substringBefore('@')
        println("setValue被調用")
        println("${className}.set已經被調用")
        name = value

    }
}
fun main(args: Array<String>)
{
    var c1 = MyClass1()
    var c2 = MyClass2()

    c1.name = "Bill"
    c2.name = "Mike"

    println(c1.name)
    println(c2.name)
}

輸出:

這裏寫圖片描述

委託屬性的要求:

只讀屬性 (val),委託必須提供一個名字叫 get 的方法並接受如下參數:

  • 接收者–必須是相同的,或者是屬性擁有者的子類型
  • 元數據–必須是 PropertyMetadata 或這它的子類型

這個函數必須返回同樣的類型作爲屬性。
可變屬性 (var),委託必須添加一個叫 set 的函數並接受如下參數:

  • 接受者–與 get() 一樣 元數據–與 get() 一樣
  • 新值–必須和屬性類型一致 或是它的字類型

3.委託類的初始化函數

如果委託類有主構造器,也可以向主構造器傳入一個初始化函數。這是可以定義一個委託函數的返回值是委託類,並在委託時指定初始化函數。

import kotlin.reflect.KProperty

//  委託類的初始化函數

public fun <T> delegate(initializer:()->T):Delegate1<T> = Delegate1(initializer)
class MyClass11
{
    //  將name屬性委託給delegate
    var name:String by delegate {
        println("MyClass1.name初始化函數調用")
        "<MyClass1>"
    }
}
class MyClass22
{
    var name:String by delegate {
        println("MyClass2.name初始化函數調用")
        "<MyClass2>"
    }
}
//  屬性委託類
class Delegate1<T>(initializer:()->T)
{
    var name:String = ""
    var className = initializer()
    operator fun getValue(thisRef:Any?, property: KProperty<*>):String
    {

        println("getValue被調用")
        println("${className}.get已經被調用")
        return name
    }
    operator fun setValue(thisRef:Any?, property: KProperty<*>, value:String)
    {
        println("setValue被調用");
        println("${className}.set已經被調用")
        name = value

    }
}
fun main(args: Array<String>)
{
    var c1 = MyClass11()
    var c2 = MyClass22()

    c1.name = "Bill"
    c2.name = "Mike"

    println(c1.name)
    println(c2.name)
}

輸出:
這裏寫圖片描述

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