引言:委託,一種設計模式,操做的對象不用本身執行,而是把工做委託給另外一個輔助的對象,其中輔助對象稱爲**
委託
**。設計模式
委託屬性的基本語法是這樣的:安全
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
複製代碼
惰性初始化是一種常見的模式,特別是在初始化過程消耗大量資源而且在使用對象時並不老是須要數據時特別有用。測試
// 定義
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
函數式線程安全的。線程
自訂對象:委託屬性發揮做用的另外一種常見用法,是用在有動態定義的屬性集的對象中,這樣的對象就稱爲自訂對象。設計
// 定義
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]
。
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中的值。