Kotlin 中有 didSet/willSet 类似物吗?

Is there a didSet/willSet analog in Kotlin?

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

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

而且我很乐意在 Kotlin 中执行此操作。然而,I can't find the proper syntax!

Kotlin中有这样的东西吗?

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

尽管 Kotlin 没有为 属性 变化观察提供内置的 Swift 风格的解决方案,您仍然可以根据您的目标以多种方式实现。

  • observable(...) delegate (in stdlib) 允许您处理 属性 更改。使用示例:

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

    这里,"bar"是属性的初始值foo,每次赋值属性后都会调用lambda,允许您观察 changes.There 也允许您阻止更改的 vetoable(...) delegate

  • 可以用custom setter换一个属性执行任意代码before/after实际值变化:

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

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

  • 通常,您可以实现自己的 property delegate,在 getValue(...)setValue(...) 函数中明确提供 属性 行为。

    为了简化您的任务,使用 ObservableProperty<T> 抽象 class 允许您实现观察 属性 变化的委托(如上面的 observablevetoable ) 例子:

    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()
     }
    

    然后你只需将 lambdas 作为 willSetdidSet 传递给它(the default argument 对它们来说是 { })。用法:

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

在任何情况下,由您来确保观察更改的代码不会再次设置 属性,因为它可能会陷入无限递归,否则您可以在观察代码是否递归调用 setter。