Kotlin進階知識(五)——重用屬性訪問的邏輯:委託屬性

引言:委託,一種設計模式,操做的對象不用本身執行,而是把工做委託給另外一個輔助的對象,其中輔助對象稱爲**委託**。設計模式

1、委託屬性的基本操做

委託屬性的基本語法是這樣的:安全

class Foo {
    var p: Type by Delegate()
}
複製代碼

屬性p將他的訪問器邏輯委託給了另外一個對象:這裏的Delegate類的一個新的實例。經過關鍵字**by對其後的表達式求值來獲取這個對象,關鍵字by能夠用於任何符合屬性委託約定規則**的對象。markdown

// 測試
class Foo {
    // 編譯器會自動生成一個輔助屬性
    private val delegate = Delegate()
    
    // 「p」的訪問都會調用對應的「delegate」的getValue和setValue方法
    var P: Int
        set(value: Int) = delegate.setValue(..., value)
        get() = delegate.getValue()
}
複製代碼

按照約定,Delegate類必須具備getValue和setValue方法(後者僅適用於可變屬性)。函數

class Delegate {
    // getValue包含了實現getter的邏輯
    operator fungetValue(...) { ... }
    // setValue包含了實現setter的邏輯
    operator fun setValue(..., value: Type)
}

class Foo {
    // 關鍵字「by」把屬性關聯上委託對象。
    var p: Type by Delegate()
}

>>> val foo = Foo()
// 經過調用delegate.getValue(...)來實現屬性的修改
>>> val oldValue = foo.p
// 經過調用delegate.setValue(..., newValue)來實現屬性的修改。
>>> foo.p = newValue
複製代碼

2、使用委託屬性:惰性初始化和「by lazy()」

惰性初始化是一種常見的模式,特別是在初始化過程消耗大量資源而且在使用對象時並不老是須要數據時特別有用。測試

  • 使用支持屬性來實現惰性初始化
// 定義
class Email {/* ... */}
fun loadEmail(person: Person): List<Email> {
    println("Load emails for ${person.name}")
    return listOf(/*...*/)
}

class Person(val name: String) {
    // 「emails」屬性用來保存數據,關聯委託
    private var _emails: List<Email>? = null
    val emails: List<Email>
        get() {
            if(_emails == null) {
                // 訪問時加載文件
                _emails = loadEmail(this)
            }
            // 若是已經加載,就直接返回
            return _emails!!
        }
}

// 測試
>>> val person = Person("Alice")
// 第一次訪問會加載郵件
>>> person.emails
Load emails for Alice
>>> person.emails
複製代碼

上述代碼使用了支持屬性技術,即一個屬性,_emails,用來存儲這個值,而另外一個emails,用來提供對屬性的讀取訪問。ui

爲了縮減代碼,Kotlin提供了標註庫函數***lazy***返回的委託。this

  • 用委託屬性來實現惰性初始化
class Person(val name: String) {
   val emails by lazy { loadEmails(this) }
}
複製代碼

lazy函數返回一個對象,該對象具備一個名爲getValue且簽名正確的方法,所以能夠把它與by關鍵字一塊兒使用來建立一個委託屬性。spa

lazy的參數是一個lambda,能夠調用它來初始化這個值。默認狀況下,lazy函數式線程安全的。線程

3、在map中保存屬性值

自訂對象:委託屬性發揮做用的另外一種常見用法,是用在有動態定義的屬性集的對象中,這樣的對象就稱爲自訂對象。設計

  • 定義一個屬性,把值存到map
// 定義
class Person {
    private val _attributes = hashMapOf<String, String>()
    fun setAttribute(attrName: String, value: String) {
        _attributes[attrName] = value
    }

    val name: String
        // 從map手動檢索屬性
        get() = _attributes["name"]!!
}

// 測試
>>> val p = Person6()
>>> val data = mapOf("name" to "Dmitry", "company" to "JetBrains")
>>> for((attrName, value) in data)
        p.setAttribute(attrName, value)
>>> println(p.name)
Dmitry
複製代碼

上述代碼中,p.name隱藏了 _attributes.getValue(p, prop)的調用,這裏變爲_attributes[prop.name]

  • 使用委託屬性把值存到map中
class Person {
    private val _attributes = hashMapOf<String, String>()
    fun setAttribute(attrName: String, value: String) {
        _attributes[attrName] = value
    }

    // 把map做爲委託屬性
    val name: String by _attributes
}
複製代碼

上述代碼中,屬性的名稱自動用做在map中的鍵,屬性值做爲map中的值。

相關文章
相關標籤/搜索