Data classes are really not made for this. Since their properties have to be declared in the primary constructor, you can't really add custom behaviour to them.
That said, if you must, you can achieve this by duplicating the properties, and then either using custom setters, or Delegates.observable
.
Here's a way to do it with custom setters, here you're gonna be accessing the publicly visible name
and age
properties, which keep the ones declared in the constructor up to date as well:
data class Person(private var _name: String, private var _age: Int) {
var name = _name
set(value) {
println("Name changed from $name to $value")
field = value // sets the backing field for `name`
_name = value // sets the `_name` property declared in the primary ctor
}
var age = _age
set(value) {
println("Age changed from $age to $value")
field = value
_age = value
}
}
Same idea, with Delegates.observable
, which does some of the work for you, here your only overhead is setting the properties declared in the constructor to the new values:
data class Person(private var _name: String, private var _age: Int) {
var name: String by Delegates.observable(_name) { prop, old, new ->
println("Name changed from $old to $new")
_name = new
}
var age: Int by Delegates.observable(_age) { prop, old, new ->
println("Age changed from $old to $new")
_age = new
}
}
Usage of either of these looks like this (the toString
will look a bit ugly with the underscores):
val sally = Person("Sally", 50)
println(sally) // Person(_name=Sally, _age=50)
sally.age = 51 // Age changed from 50 to 51
println(sally) // Person(_name=Sally, _age=51)
println(sally.name) // Sally
println(sally.age) // 51
Edit to answer the question below:
If you didn't need your class to be a data class, the following would probably be the simplest solution:
class Person(name: String, age: Int) {
var name: String by Delegates.observable(name) { _, old, new ->
println("Name changed from $old to $new")
}
var age: Int by Delegates.observable(age) { _, old, new ->
println("Age changed from $old to $new")
}
}
This way you still have a constructor that takes the name and age as parameters, but they get assigned to the properties that are inside the body of the class. This is not possible with a data class, because every constructor parameter of a data class has to be a property as well (marked val
or var
). For more, you can see the documentation about constructors, properties, and data classes.