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?
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
}
object
is translated into a class with a private constructor and a static field called INSTANCE
where the only instance is stored when this class is loaded, so replacing Class.forName(ownerClassName).newInstance()
with
Class.forName(ownerClassName).getDeclaredField("INSTANCE").get(null)
should work.
Javadoc:
Class#forName
Class#getDeclaredField
Field#get
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With