引言:委托,一种设计模式,操做的对象不用本身执行,而是把工做委托给另外一个辅助的对象,其中辅助对象称为**
委托
**。设计模式
委托属性的基本语法是这样的:安全
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中的值。