J'adore cette syntaxe Swift; elle est très utile pour beaucoup de choses:
var foo: Bar = Bar() {
willSet {
baz.prepareToDoTheThing()
}
didSet {
baz.doTheThing()
}
}
et j'adorerais faire ça à Kotlin. Cependant, je ne trouve pas la syntaxe appropriée !
Y a-t-il quelque chose dans Kotlin comme ça?
var foo: Bar = Bar()
willSet() {
baz.prepareToDoTheThing()
}
didSet() {
baz.doTheThing()
}
Bien que Kotlin ne fournisse pas de solution intégrée de style Swift pour l'observation des changements de propriété, vous pouvez toujours le faire de plusieurs manières en fonction de votre objectif.
Il y a observable(...)
délégué (dans stdlib) qui vous permet de gérer les changements de propriété. Exemple d'utilisation:
var foo: String by Delegates.observable("bar") { property, old, new ->
println("$property has changed from $old to $new")
}
Ici, "bar"
Est la valeur initiale de la propriété foo
, et le lambda est appelé à chaque fois après que la propriété est affectée, ce qui vous permet d'observer les changements. Il y a aussi vetoable(...)
delegate qui vous permet d'empêcher le changement.
Vous pouvez utiliser setter personnalisé pour une propriété pour exécuter du code arbitraire avant/après le changement de valeur réelle:
var foo: String = "foo"
set(value: String) {
baz.prepareToDoTheThing()
field = value
baz.doTheThing()
}
Comme @ KirillRakhman l'a noté, cette solution est assez efficace car elle n'introduit pas de surcharge dans les appels de méthode et les objets, bien que le code soit un peu dupliqué en cas de plusieurs propriétés.
En général, vous pouvez implémenter votre propre délégué de propriété , en fournissant explicitement le comportement de la propriété dans les fonctions getValue(...)
et setValue(...)
.
Pour simplifier votre tâche, utilisez ObservableProperty<T>
classe abstraite qui vous permet d'implémenter des délégués qui observent les changements de propriété (comme observable
et vetoable
ci-dessus) Exemple:
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()
}
}
Pour votre commodité, vous pouvez écrire une fonction qui crée l'objet délégué:
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()
}
Ensuite, vous lui passez juste des lambdas sous la forme willSet
et didSet
( l'argument par défaut pour eux est { }
). Usage:
var foo: String by observing("bar", willSet = {
baz.prepareToDoTheThing()
}, didSet = {
baz.doTheThing()
})
var baq: String by observing("bar", didSet = { println(baq) })
Dans tous les cas, c'est à vous de vous assurer que le code observant les changements ne redéfinit pas la propriété car il tombera probablement en récursion infinie, sinon vous pouvez le vérifier dans le code d'observation si le setter est appelé récursivement.