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中的值。

相关文章
相关标签/搜索