Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a didSet/willSet analog in Kotlin?

I love this Swift syntax; it's very helpful for many things:

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

and I'd love to do this in Kotlin. However, I can't find the proper syntax!

Is there anything in Kotlin like this?

var foo: Bar = Bar()     willSet() {         baz.prepareToDoTheThing()     }     didSet() {         baz.doTheThing()     } 
like image 872
Ky. Avatar asked Oct 04 '16 00:10

Ky.


People also ask

What is didSet?

didSet is called right after the data is stored and it has a default constant oldValue which shows the previous value that is overwritten. willSet and didSet cannot be used at computed property since set function has already covered their functionalities and getter only stored property cannot be set actually!

What does didSet mean in Swift?

Property Observers in Swift. In Swift, you can attach property observers to variables to run code when the variable changes. These property observers are called willSet and didSet. The former runs right before the property changes, and the latter runs right after the changes were made.

What are property observers in Swift?

Property Observers. Property observers observe and respond to changes in a property's value. Property observers are called every time a property's value is set, even if the new value is the same as the property's current value.


1 Answers

Although Kotlin doesn't provide a built-in Swift-style solution for property changes observation, you can still do it in several ways depending on what your goal is.

  • There is observable(...) delegate (in stdlib) that allows you to handle the property changes. Usage example:

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

    Here, "bar" is the initial value for property foo, and the lambda is called each time after the property is assigned, allowing you to observe the changes.There's also vetoable(...) delegate that allows you to prevent the change.

  • You can use custom setter for a property to execute arbitrary code before/after actual value change:

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

    As @KirillRakhman noted, this solution is quite efficient since it introduces no overhead in method calls and objects, though the code will be a little duplicated in case of several properties.

  • In general, you can implement your own property delegate, explicitly providing the property behavior in getValue(...) and setValue(...) functions.

    To simplify your task, use ObservableProperty<T> abstract class that allows you to implement delegates that observe property changes (like observable and vetoable above) Example:

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

    For your convenience, you can write a function that creates the delegate object:

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

    Then you just pass lambdas to it as willSet and didSet (the default argument for them is { }). Usage:

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

In any case, it's up to you to make sure that the code observing the changes doesn't set the property again as it will likely fall into infinite recursion, or else you might check it in the observing code whether the setter is called recursively.

like image 141
hotkey Avatar answered Sep 19 '22 19:09

hotkey