Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 490
P.J.Meisch Avatar asked Sep 13 '25 07:09

P.J.Meisch


1 Answers

object is not an instance of declaring class

Just as in Java, you need to pass the object itself as the first parameter when calling reflective methods.

The first parameter to call should be the companion object, as that is the object whose property you are trying to modify.

You are passing the companion's class object instead of the companion object itself.

A companion object is accessible either via ClassName.Companion, or when using further reflection, through KClass#companionObjectInstance.

companionProp.setter.call(WithProperty.Companion, "hello")
companionProp.setter.call(obj::class.companionObjectInstance, "hello")
companionProp.setter.call(WithProperty::class.companionObjectInstance, "hello")

When run, both variants print hello world as intended.

Keep in mind that Foo.Companion will result in a compile error if the companion object does not exist while the reflective variant will return null instead.

like image 112
Salem Avatar answered Sep 14 '25 20:09

Salem