Kotlin data class: how to read the value of a property if I don't know its name at compile time?

How can I find out the value of a property in an instance of a Kotlin data class if the name of the property is known only at run time?

+8
source share
3 answers

Here is a function for reading a property from an instance of a class by the name of the property (throws an exception if the property is not found, but you can change this behavior):

import kotlin.reflect.KProperty1 import kotlin.reflect.full.memberProperties @Suppress("UNCHECKED_CAST") fun <R> readInstanceProperty(instance: Any, propertyName: String): R { val property = instance::class.memberProperties // don't cast here to R, it would succeed silently .first { it.name == propertyName } as KProperty1<Any, *> // force a invalid cast exception if incorrect type here return property.get(instance) as R } 

usage example:

 // some data class data class MyData(val name: String, val age: Int) val sample = MyData("Fred", 33) // and reading property "name" from an instance... val name: String = readInstanceProperty(sample, "name") // and reading property "age" placing the type on the function call... val age = readInstanceProperty<Int>(sample, "age") println(name) // Fred println(age) // 33 

Note. Using Kotlin reflection requires an org.jetbrains.kotlin:kotlin-reflect dependency org.jetbrains.kotlin:kotlin-reflect reflection.

+9
source

You can do this through reflection , and this is the same for data classes and regular classes.

The first option is to just use Java reflection:

 val name = obj.javaClass .getMethod("getName") // to get property called `name` .invoke(obj) 

You can even make an extension function:

 inline fun <reified T : Any> Any.getThroughReflection(propertyName: String): T? { val getterName = "get" + propertyName.capitalize() return try { javaClass.getMethod(getterName).invoke(this) as? T } catch (e: NoSuchMethodException) { null } } 

He calls a public getter. To get the value of a private property, you can change this code with getDeclaredMethod and setAccessible . This will also work for Java objects with corresponding getters (but it skips convention is and has getters for boolean ).

And use:

 data class Person(val name: String, val employed: Boolean) val p = Person("Jane", true) val name = p.getThroughReflection<String>("name") val employed = p.getThroughReflection<Boolean>("employed") println("$name - $employed") // Jane - true 


The second option includes the use of the kotlin-reflect library, which you must add to your project separately, here is its documentation . This will allow you to get the actual value of the Kotlin property, ignoring Java-getters.

You can use javaClass.kotlin to get the current token of the Kotlin class, and then you get the property from it:

 val name = p.javaClass.kotlin.memberProperties.first { it.name == "name" }.get(p) 

This solution will only work for Kotlin classes, not Java, but much more reliable if you need to work with Kotlin classes: it does not depend on the underlying implementation.

+14
source

The answers above did not help me, so I created an extension function for this:

 @Throws(IllegalAccessException::class, ClassCastException::class) inline fun <reified T> Any.getField(fieldName: String): T? { this::class.memberProperties.forEach { kCallable -> if (fieldName == kCallable.name) { return kCallable.getter.call(this) as T? } } return null } 

This is an example call:

 val valueNeeded: String? = yourObject.getField<String>("exampleFieldName") 

Also include this in your build.gradle application:

 implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" 
-1
source

Source: https://habr.com/ru/post/1243464/


All Articles