reflectionkotlin

How do I use Kotlin object by reflection


Let's say I need to set property A given by a String in Kotlin object O given by a String by reflection. If O was a class I could do something like this (disregard it has no sense):

fun setValue(ownerClassName: String, fieldName: String, value : Any) {
    val enclosingClass = Class.forName(ownerClassName).newInstance()
    val enclosingClassField = enclosingClass.javaClass.getDeclaredField(fieldName)
    enclosingClassField.isAccessible = true
    enclosingClassField.set(enclosingClass, value)
}

But how would I do it if O is an object?


Solution

  • KClass has an objectInstance field:

    Class.forName(ownerClassName).kotlin.objectInstance
    

    This is built into Kotlin reflection.

    Returns: The instance of the object declaration, or null if this class is not an object declaration.

    This would be even nicer if KClass had a forName method, but sadly it does not (yet), so we need to instead get the (Java) Class and convert it to KClass.

    You can get a KClass instance from a Class by using the .kotlin extension property.

    Then you can continue with the rest of your code. I converted this to Kotlin's reflection library:

    val kClass = Class.forName(ownerClassName).kotlin
    // Get the object OR a new instance if it doesn't exist
    val instance = kClass.objectInstance ?: kClass.java.newInstance()
    
    val member = kClass.memberProperties
    // Has to be a mutable property, otherwise we can't set it
            .filterIsInstance<KMutableProperty<*>>()
    // Check the name
            .filter { it.name == fieldName }
            .firstOrNull()
    
    // Set the property
    member?.setter?.call(instance, value)
    

    Here is a working test:

    object TestObject {
        var field = 3
    }
    
    fun setValue(ownerClassName: String, fieldName: String, value: Any) {
        val kClass = Class.forName(ownerClassName).kotlin
        val instance = kClass.objectInstance ?: kClass.java.newInstance()
    
        val member = kClass.memberProperties.filterIsInstance<KMutableProperty<*>>()
                .firstOrNull { it.name == fieldName }
    
        member?.setter?.call(instance, value)
    }
    
    fun main(args: Array<String>) {
        println(TestObject.field) // 3
        setValue("some.package.TestObject", "field", 4)
        println(TestObject.field) // 4
    }