Home > Software design >  Accessing an objects value by fieldName in Kotlin
Accessing an objects value by fieldName in Kotlin

Time:10-15

I know I could do something like this:

data class Foo(val bar: String)

val foo = Foo("bar")

foo::class.java.getDeclaredField("bar").let {  it.trySetAccessible(); it.get(foo) }

For this tho I need to use class.java.getDeclaredField("bar") and other Java things like trySetAccessible() and get.

Is it possible to do this in a more Kotlin way?

CodePudding user response:

Kotlin reflection doesn't have functions like getDeclaredField(). Instead, it has functions returning collections, like getMemberProperties(), and you can use the Iterable extension functions to find what you need. Pass an instance of the class as the first parameter of the getter/setter you want to use.

    val value = Foo::class.memberProperties
        .first { it.name == "bar" }
        .also { it.isAccessible = true } // if it's not visible
        .getter(foo)

Note to do this without casting you need to use Foo::class instead of foo::class because the latter is a Class<out Foo> so the getters it returns are not usable due to variance.

But if you're using reflection, you may not know the class you're working with, in which case you'd have to cast the returned property before using it.

    val value = foo::class.memberProperties
        .first { it.name == "bar" }
        .also { it.isAccessible = true }
        .let { it as KProperty1<in Any, *> }
        .getter(foo)

// or

    val value = (foo::class as KClass<in Any>).memberProperties
        .first { it.name == "bar" }
        .also { it.isAccessible = true }
        .getter(foo)

CodePudding user response:

You can do it with Kotlin reflection like this:

val prop = (foo::class as KClass<Foo>).memberProperties.single { it.name == "bar" }
println(prop.get(foo))

Note that contrary to Java, you can't use this technique to set the value of read-only field (val). The reason is: in Kotlin we don't really deal with fields, but with properties. And property is a getter and optionally a setter. If there is no setter then there is nothing really to invoke. val may not be backed by a field, it may not hold any value at all. In such case it would not make sense to even consider setting the value of a property.

Of course, Kotlin reflection API could provide optional access to fields if property is backed by one, but right now I believe there is no representation for fields in public API.

  • Related