Kotlin基础学习(二十)—— 委托属性

委托属性:依赖于约定的功能,是 Kotlin 中最独特和最强大的功能之一。

一、基本知识

语法:

val/var <属性名>: <类型> by <表达式>  

其中,在 by 后面的表达式是委托,属性对应的 get() 与 set() 会被委托给它的 getValue() 与 setValue() 方法。按照约定,表达式类必须具有 getValue 和 setValue 方法(setValue 方法仅适用于可变属性)

import kotlin.reflect.KProperty

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

class Delegate {
    operator fun setValue(example: Example, property: KProperty<*>, s: String) {
        println("$s has been assigned to '${property.name}' in $example.")
    }

    operator fun getValue(example: Example, property: KProperty<*>): String {
        return "$example, thank you for delegating '${property.name}' to me!"
    }
}

fun main(args: Array<String>){
    val example = Example()
    example.p = "hello world!!!"
    println(example.p)
}

结果:

hello world!!! has been assigned to ‘p’ in Example@1f17ae12.
Example@1f17ae12, thank you for delegating ‘p’ to me!

二、使用委托属性

1、惰性初始化 和 “by lazy()” 【延迟属性】

惰性初始化,类似于懒人模式,直到在第一次访问该属性的时候,才根据需要创建对象的一部分。

class Person(val name: String){
	private var _emails: List<Email>? = null
	val emails: List<Email>
		get(){
			if(_emails == null){
				_emails = loadEmails(this)
			}
			return _emails!!
		}
}
fun main(args: Array<String>){
	val p = Person("Alice")
	p.emails
}

支持属性:类中有一个属性,_emails,而另一个属性 emails,用来提供对属性的读取访问。_emails 可以为空,而 emails 为非空。

上述代码并不是线程安全的,在 Kotlin 中提供的解决方案:使用委托属性来实现惰性初始化,默认情况下,对于 lazy 属性的求值是同步锁的

val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

//val lazyValue: String by lazy ({
//    println("computed!")
//    "Hello"
//})

fun main() {
    println(lazyValue)
    println(lazyValue)
}

结果:

computed!
Hello
Hello

lazy() 是接收一个 lambda 并返回一个 Lazy< T > 实例的函数,返回的实例可以作为实现延迟属性的委托:第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式 并 记录结果,后续调用 get() 就只是返回记录的结果。

2、可观察属性 Observable

Delegates.observable() 接受两个参数:初始值与修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值与新值:

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main() {
    val user = User()
    user.name = "first"
    user.name = "second"
}

结果:

<no name> -> first
first -> second

三、实现委托属性

从 Kotlin 1.4 开始,一个属性可以把它的 getter 与 setter 委托给另一个属性。这种委托对于顶层和类的属性(成员和扩展)都可用。该委托属性可以为:1)顶层属性;2)同一个类的成员或扩展属性; 3)另一个类的成员或扩展属性。

实例:

写法1:当一个人的年龄或工资发生变化时,进行通知

import java.beans.PropertyChangeListener
import java.beans.PropertyChangeSupport

open class PropertyChangeAware {
    protected val changeSupport = PropertyChangeSupport(this)
    fun addPropertyChangeListener(listener: PropertyChangeListener) {
        changeSupport.addPropertyChangeListener(listener)
    }
    fun removePropertyChangeListener(listener: PropertyChangeListener){
        changeSupport.removePropertyChangeListener(listener)
    }
}
//Person类,当一个人的年龄或工资发生变化时,这个类将通知它的监听器
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
    var age : Int = age
        set(newValue) {
            val oldVaule = field
            field = newValue
            changeSupport.firePropertyChange("age", oldVaule, newValue)
        }
    var salary : Int = salary
        set(newValue) {
            val oldValue = field
            field = newValue
            changeSupport.firePropertyChange("salary", oldValue, newValue)
        }
}
fun main(args:Array<String>){
    val p = Person("Zxy", 25, 10000)
    p.addPropertyChangeListener(
            PropertyChangeListener { event ->
                println("Property ${event.propertyName} changed " +
                "from ${event.oldValue} to ${event.newValue}")
            }
    )
    p.age = 29
    p.salary = 30000
}

可以看出,setter 中有很多重复的代码,提取一个类来存储,代码如下:

写法2:当一个人的年龄或工资发生变化时,进行通知。简化写法1

import java.beans.PropertyChangeListener
import java.beans.PropertyChangeSupport

open class PropertyChangeAware {
    protected val changeSupport = PropertyChangeSupport(this)
    fun addPropertyChangeListener(listener: PropertyChangeListener) {
        changeSupport.addPropertyChangeListener(listener)
    }
    fun removePropertyChangeListener(listener: PropertyChangeListener){
        changeSupport.removePropertyChangeListener(listener)
    }
}
class ObservableProperty(val propName:String, var propValue:Int, val changeSupport: PropertyChangeSupport){
    fun getValue():Int = propValue
    fun setValue(newValue: Int){
        val oldValue = propValue
        propValue = newValue
        changeSupport.firePropertyChange(propName, oldValue, newValue)
    }
}
//Person类,当一个人的年龄或工资发生变化时,这个类将通知它的监听器
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
    val _age = ObservableProperty("age", age, changeSupport)
    var age: Int
        get() = _age.getValue()
        set(value) { _age.setValue(value) }

    val _salary = ObservableProperty("salary", salary, changeSupport)
    var salary: Int
        get() = _salary.getValue()
        set(value) { _salary.setValue(value) }
}
fun main(args:Array<String>){
    val p = Person("Zxy", 25, 10000)
    p.addPropertyChangeListener(
            PropertyChangeListener { event ->
                println("Property ${event.propertyName} changed " +
                "from ${event.oldValue} to ${event.newValue}")
            }
    )
    p.age = 29
    p.salary = 30000
}

上述代码将属性的 getter 和 setter 委托给 ObservableProperty 类,在 Kotlin 中属性委托功能可以让 程序员 摆脱这些样板代码。

使用 ObservableProperty 类作为属性委托

写法3:当一个人的年龄或工资发生变化时,进行通知。使用属性委托

import java.beans.PropertyChangeListener
import java.beans.PropertyChangeSupport
import kotlin.reflect.KProperty

open class PropertyChangeAware {
    protected val changeSupport = PropertyChangeSupport(this)
    fun addPropertyChangeListener(listener: PropertyChangeListener) {
        changeSupport.addPropertyChangeListener(listener)
    }
    fun removePropertyChangeListener(listener: PropertyChangeListener){
        changeSupport.removePropertyChangeListener(listener)
    }
}
class ObservableProperty(var propValue:Int, val changeSupport: PropertyChangeSupport){
    // p,用来接收属性的实例;prop,用于表示属性本身
    operator fun getValue(p: Person, prop: KProperty<*>):Int = propValue
    operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int){
        val oldValue = propValue
        propValue = newValue
        changeSupport.firePropertyChange(prop.name, oldValue, newValue)
    }
}
//Person类,当一个人的年龄或工资发生变化时,这个类将通知它的监听器
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
    var age: Int by ObservableProperty(age, changeSupport)
    var salary: Int by ObservableProperty(salary, changeSupport)
}
fun main(args:Array<String>){
    val p = Person("Zxy", 25, 10000)
    p.addPropertyChangeListener(
            PropertyChangeListener { event ->
                println("Property ${event.propertyName} changed " +
                "from ${event.oldValue} to ${event.newValue}")
            }
    )
    p.age = 29
    p.salary = 30000
}

写法4:不用手动实现 ObservableProperty 类,而是使用 Kotlin 标准库,它已经包含了类似于 ObservableProperty 的类。

标准库和 PropertyChangeSupport 类没有耦合,因此需要传递一个 lambda,来告诉它如何通知属性值的更改

import java.beans.PropertyChangeListener
import java.beans.PropertyChangeSupport
import kotlin.properties.Delegates
import kotlin.reflect.KProperty

open class PropertyChangeAware {
    protected val changeSupport = PropertyChangeSupport(this)
    fun addPropertyChangeListener(listener: PropertyChangeListener) {
        changeSupport.addPropertyChangeListener(listener)
    }
    fun removePropertyChangeListener(listener: PropertyChangeListener){
        changeSupport.removePropertyChangeListener(listener)
    }
}

//Person类,当一个人的年龄或工资发生变化时,这个类将通知它的监听器
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
    private val observer = {
        prop: KProperty<*>, oldValue: Int, newValue: Int -> changeSupport.firePropertyChange(prop.name, oldValue, newValue)
    }
    var age: Int by Delegates.observable(age, observer)
    var salary: Int by Delegates.observable(salary, observer)
}
fun main(args:Array<String>){
    val p = Person("Zxy", 25, 10000)
    p.addPropertyChangeListener(
            PropertyChangeListener { event ->
                println("Property ${event.propertyName} changed " +
                "from ${event.oldValue} to ${event.newValue}")
            }
    )
    p.age = 29
    p.salary = 50000
}

by 右边的表达式,其值,只要是能够被编译器用正确的参数类型来调用 getValue 和 setValue 的对象即可。

四、在 map 中保存属性值

写法1:定义属性,将值存入 map

class Person{
    private val _attributes = hashMapOf<String, String>()
    fun setAttribute(attrName:String, value:String){
        _attributes[attrName] = value
    }
    val name:String
        get() = _attributes["name"]!!
}
fun main(args:Array<String>){
    val p = Person()
    val data = mapOf("name" to "Zxy", "company" to "psbc")
    for((attrName, value) in data)
        p.setAttribute(attrName, value)
    println(p.name)
}

写法2:使用委托属性把值存到 map 中

class Person{
    private val _attributes = hashMapOf<String, String>()
    fun setAttribute(attrName:String, value:String){
        _attributes[attrName] = value
    }
    val name:String by _attributes  //由于标准库已在 Map 和 MutableMap 接口上定义了 getValue 和 setValue 扩展函数,所以可以把 map 作为委托属性
}
fun main(args:Array<String>){
    val p = Person()
    val data = mapOf("name" to "Zxy", "company" to "psbc")
    for((attrName, value) in data)
        p.setAttribute(attrName, value)
    println(p.name)
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值