Is there a way to programatically access the parameters of a data class
in kotlin?
data class Foo(
val a: Int,
val b: Double,
val c: Boolean,
val d: String,
) {
fun doubleA(): Int {
return (a * 2)
}
}
val something = Foo(1, 58.1, true, "Hey There")
val parameters = listOf("a", "b", "c", "d")
for (parameter in parameters) {
-> Access "something" with "parameter"
}
CodePudding user response:
Outside of reflection no, the only special thing a data class provides is a bunch of componentN
functions that accesses the properties in the order they're declared. This is what's used to do destructuring declarations, so you could do this if you want:
val (a, b, c, d) = something
val parameters = listOf(a, b, c, d)
Really that's reading each property into a local val
, then creating a list from all those values, so it's not super efficient or anything - but it's concise, if it's what you want to do! Also this is effectively read-only since you're just reading the current values of those properties into a list - you don't have references to the properties themselves in your loop, so if you wanted to do things like updating the values in the data class, this isn't the way to go.
You could do something like that programmatically with reflection - but at that point, you need to ask if this the right way to do whatever you're trying to do. This approach is basically stepping outside the type system - you're creating a general list of stuff instead of the structured data the data class provides. Maybe that's the right call! Or maybe you want to stay within the type system, and explicitly write code to handle each data class specifically, e.g. creating a sealed class hierarchy of all the data classes you need to handle. It really depends on what you're doing though!
CodePudding user response:
You can use the Reflection feature to do that at The Runtime
fun accessDataClassPropertyByName(clazz: Any, name: String): Any? {
return clazz::class.members.firstOrNull { it.name == name }?.call(clazz)
}
fun main() {
val something = Foo(1, 58.1, true, "Hey There")
val parameters = listOf("a", "b", "c", "d")
for (parameter in parameters) {
println(accessDataClassPropertyByName(something, parameter))
}
}
Output
1
58.1
true
Hey There
Another solution is to create an Annotation Processor that generates a class with function or function extensions at compile time to take string and call the property, you need to generate extension like this
fun Foo.accessWithName(name : String) : Any? {
return when (name) {
"a" -> a
"b" -> b
"c" -> c
"d" -> d
else -> null
}
}