Kotlin 中有 didSet/willSet 类似吗?

问题描述 投票:0回答:1

我喜欢这种 Swift 语法;这对很多事情都非常有帮助:

var foo: Bar = Bar() {
    willSet {
        baz.prepareToDoTheThing()
    }
    didSet {
        baz.doTheThing()
    }
}

我很乐意在 Kotlin 中做到这一点。但是,我找不到正确的语法

Kotlin 有这样的东西吗?

properties kotlin didset
1个回答
103
投票

尽管 Kotlin 没有提供用于属性更改观察的内置 Swift 风格解决方案,但您仍然可以根据您的目标通过多种方式实现。

  • observable(...)
    委托 (在 stdlib 中) 允许您处理属性更改。使用示例:

    var foo: String by Delegates.observable("bar") { property, old, new ->
        println("$property has changed from $old to $new")
    }
    

    这里,

    "bar"
    是属性
    foo
    的初始值,每次分配属性后都会调用lambda,让你可以观察到变化。还有
    vetoable(...)
    委托
    可以让你防止改变。

  • 您可以使用属性的自定义设置器在实际值更改之前/之后执行任意代码:

    var foo: String = "foo"
        set(value: String) {
            baz.prepareToDoTheThing()
            field = value
            baz.doTheThing()
        }
    

    正如 @KirillRakhman 所指出的,这个解决方案非常有效,因为它不会在方法调用和对象中引入任何开销,尽管在有多个属性的情况下代码会有点重复。

  • 一般来说,您可以实现自己的属性委托,在

    getValue(...)
    setValue(...)
    函数中显式提供属性行为。

    为了简化您的任务,请使用

    ObservableProperty<T>
    抽象类,它允许您实现观察属性更改的委托(如上面的
    observable
    vetoable
    )示例:

    var foo: String by object : ObservableProperty<String>("bar") {
        override fun beforeChange(property: KProperty<*>, oldValue: String, newValue: String): Boolean {
            baz.prepareToDoTheThing()
            return true // return false if you don't want the change
        }
    
        override fun afterChange(property: KProperty<*>, oldValue: String, newValue: String) {
            baz.doTheThing()
        }
    }
    

    为了方便起见,您可以编写一个创建委托对象的函数:

    fun <T> observing(initialValue: T,
                      willSet: () -> Unit = { },
                      didSet: () -> Unit = { }
    ) = object : ObservableProperty<T>(initialValue) {
        override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean =
                true.apply { willSet() }
    
        override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = didSet()
     }
    

    然后您只需将 lambda 作为

    willSet
    didSet
    传递给它(它们的默认参数
    { }
    )。用途:

    var foo: String by observing("bar", willSet = {
        baz.prepareToDoTheThing()
    }, didSet = {
        baz.doTheThing()
    })
    
    var baq: String by observing("bar", didSet = { println(baq) })
    

在任何情况下,您都需要确保观察更改的代码不会再次设置属性,因为它可能会陷入无限递归,否则您可以在观察代码中检查 setter 是否被递归调用.

© www.soinside.com 2019 - 2024. All rights reserved.