I often have a class with properties that are initialized at instantiation, like
class C {
var x = 10
var y = v * 2 // v is some variable
}
val c = C()
then properties of c are changed, and later I need to re-initialize the properties (so that c.x is 10 again and c.y is v*2, where the value of v may have changed).
My current approach is to initialize the properties with dummy values (or alternatively use lateinit and type annotations), and assign the desired values in an extra function ini like
class C {
var x = 0
var y = 0
init {
ini()
}
fun ini() {
x = 10
y = v * 2
}
}
then I call c.ini() to re-initialize.
Is there a better (more succinct) way that avoids the dummy values?
Note that in JavaScript I can simply write
class C {
constructor() {
this.ini()
}
ini() {
this.x = 10
this.y = v * 2
}
}
CodePudding user response:
What you are doing right now is totally fine. I wouldn't change it.
If you want to avoid the placeholder values of 0, or if the type of the property has no placeholder value that make sense, you can store the initial values in private lambdas:
class C {
private val xInit = { 10 }
private val yInit = { v * 2 }
var x = xInit()
var y = yInit()
fun reset() {
x = xInit()
y = yInit()
}
}
If you really want to reduce the boilerplate, I can only think of this rather hacky and slow solution that relies on reflection. This could be usable if the instances don't need to be reset that often, and you have lots of these classes with resettable properties, and you just hate writing boilerplate reset methods.
class Resettable<T>(val supplier: () -> T) {
var wrapped = supplier()
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return wrapped
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
wrapped = value
}
fun reset() {
wrapped = supplier()
}
}
fun <T: Any> T.reset() {
// look for properties delegated with Resettable
this::class.memberProperties.mapNotNull {
it.isAccessible = true
(it as KProperty1<T, *>).getDelegate(this)
}.filterIsInstance<Resettable<*>>().forEach {
it.reset()
}
}
Now you just need to do:
var v = 10
class C {
var x by Resettable { 10 }
var y by Resettable { v * 2 }
}
fun main() {
val x = C()
println(x.y) // 20
v = 20
x.reset()
println(x.y) // 40
v = 40
x.reset()
println(x.y) // 80
}
Note that the first reset call would load all the kotlin reflection classes, and would take a noticeable amount of time.
