How can I set a property of a companion object in Kotlin via reflection?

Viewed 5526

When I have a class that has a companion object, is it possible to set a property in this companion object using reflection? I can do it with normal properties, but fail on companion objects:

import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.memberProperties

class WithProperty {
    lateinit var prop: String

    companion object {
        lateinit var companionProp: String
    }

    fun test() = "$companionProp $prop"
}

fun main(args: Array<String>) {
    val obj = WithProperty()

    val prop = obj::class.memberProperties.filter { it.name == "prop" }.first()
    if (prop is KMutableProperty<*>) {
        prop.setter.call(obj, "world")
    }

    val companion = obj::class.companionObject
    if (companion != null) {
        val companionProp = companion.memberProperties.filter { it.name == "companionProp" }.first()
        if (companionProp is KMutableProperty<*>) {
            companionProp.setter.call(companionProp, "hello") // <-- what must go here as first argument?
        }
    }

    println(obj.test())
}

Calling the setter for the normal property works as it should, but when I call companionProp.setter.call(companionProp, "hello") I get

Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class

What do I have to pass as first argument to call() to succeed?

Edit: I wrote companionPropas first argument, but that definitely is wrong, I actually tried with the companion object, but that is not working as well.

3 Answers

You can solve the same problem using java reflection.

Companion class:

class Example {
    companion object {
        val EXAMPLE_VALUE = "initial"
    }
}

Update a property using java reflection:

val field = Example::class.java.getDeclaredField("EXAMPLE_VALUE")
field.isAccessible = true
field.set(null, "replaced")

Tested with Kotlin 1.5.30 on Android 12:

Log.d("test-companion", Example.EXAMPLE_VALUE) // outputs "replaced"

WARNING: I'm not sure if java reflection is reliable for this case. It assumes some implementation details of Kotlin complier which could change in a future version. But the solution should be fine for a quick workaround. I used it to verify a bug fix on customer side before the next release of my library.

Related