Ответ 1
Хотя Kotlin не предоставляет встроенного Swift-стиля для наблюдения за изменениями свойств, вы все равно можете сделать это несколькими способами в зависимости от вашей цели.
-
observable(...)
delegate (в stdlib), который позволяет обрабатывать изменения свойств. Пример использования:var foo: String by Delegates.observable("bar") { property, old, new -> println("$property has changed from $old to $new") }
Здесь
"bar"
- начальное значение для свойстваfoo
, и lambda вызывается каждый раз после назначения свойства, что позволяет вам наблюдать за изменениями. Такжеvetoable(...)
delegate, который позволяет предотвратить изменение. -
Вы можете использовать настраиваемый сеттер для свойства для выполнения произвольного кода до/после изменения фактического значения:
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() }
Затем вы просто передаете lambdas ему как
willSet
иdidSet
(аргумент по умолчанию для них{ }
). Использование:var foo: String by observing("bar", willSet = { baz.prepareToDoTheThing() }, didSet = { baz.doTheThing() }) var baq: String by observing("bar", didSet = { println(baq) })
В любом случае, вам нужно убедиться, что код, наблюдающий за изменениями, не устанавливает свойство снова, поскольку оно, вероятно, попадет в бесконечную рекурсию, или вы можете проверить его в коде наблюдения, является ли сеттер называемый рекурсивно.