I am looking for a way, without having to install a new library, to iterate through a Kotlin data class
and to change the values according to their type.
The implementation I would like is the following:
The data class
I need:
data class Thing(
var name: String,
var a: Double,
var b: Double,
var c: Double,
var d: Double,
... hundreds of properties of type Double
)
I need to change all values of type Double
by a scalar number like in the following pseudo code:
something = Thing("stuff", 0.96, 0.72, 0.55, 81.4)
scalar = 3.75
for (property, value in something.properties_n_values())
{
if property type is not a String:
something[property] = value * scalar
}
print(something)
// returns Thing("stuff", 3.6, 2.7, 2.06, 305.25)
The reasons why I choose a data class
instead of a Map
or any customized Class
:
data class
has already setter and getter implemented.data class
is able to contain different type of values, while as far as I know a Map
is able to contain an only type.data class
element will be a line record from a CSV file.Kotlin version : 231-1.8.20-IJ8109.175
On the JVM, you can use reflection,
val something = Thing("stuff", 0.96, 0.72, 0.55, 81.4)
val scalar = 3.75
val properties = something::class.memberProperties.mapNotNull {
// this unchecked cast is checked by the takeIf on the next line
(it as? KMutableProperty1<Thing, Double>)
?.takeIf { it.returnType == typeOf<Double>() }
}
for (property in properties) {
property.set(something, property.get(something) * scalar)
}
But if speed is important to you, I'd suggest you store all the Double
properties internally in a MutableArray
, and expose separate getters/setters/secondary constructors for using them individually.
If you don't want to install kotlin.reflect.full, you can also do this with Java reflection:
val properties = something.javaClass.declaredFields.filter {
!Modifier.isFinal(it.modifiers) && !Modifier.isStatic(it.modifiers) &&
it.type == Double::class.java
}
for (property in properties) {
property.isAccessible = true
property.set(something, property.get(something) as Double * scalar)
}
This will find all non-final non-static fields and set them, so (unlike with Kotlin reflection) this will not set properties that are not backed by a field.