I can access a private val value using reflection as below
fun main() {
val mainClass = MainClass()
val f = MainClass::class.memberProperties.find { it.name == "info" }
f?.let {
it.isAccessible = true
val w = it.get(mainClass) as String
println(w)
}
}
class MainClass {
private val info: String = "Hello"
}
But if I want to change info
, how could I do it using reflection?
In short, you have to use Java reflection APIs in this case, and here is how to do it:
fun main() {
val mainClass = MainClass()
val f = MainClass::class.java.getDeclaredField("info")
f.isAccessible = true
f.set(mainClass, "set from reflection")
mainClass.printInfo() // Prints "set from reflection"
}
class MainClass {
private val info: String = "Hello"
fun printInfo() = println(info)
}
It is not possible to do with Kotlin reflection APIs since no setter code is generated for a read-only (val
) property. So to change it, we need to use Java reflection APIs which is more low-level. First, we use Tools -> Kotlin -> Show Kotlin Bytecode to see what the generated bytecode looks like. Then we see this:
// ================MainClass.class =================
// class version 50.0 (50)
// access flags 0x31
public final class MainClass {
// access flags 0x12
private final Ljava/lang/String; info = "Hello"
// ...
i.e that the info
fields in the MainClass
Kotlin class causes the compiler to emit JVM code for a regular MainClass
Java class with a final String info
field. So to change it, we can use Java reflection APIs, as in the code above.
If the field would have been private var
you would be able to Use Kotlin reflection APIs like this:
f?.let {
val mutableProp = it as KMutableProperty<*>
it.isAccessible = true
mutableProp.setter.call(mainClass, "set from Kotlin reflection")
val w = it.get(mainClass) as String
println(w)
}
but if you try this with private val
you will get the below exception
Exception in thread "main" java.lang.ClassCastException: class kotlin.reflect.jvm.internal.KProperty1Impl cannot be cast to class kotlin.reflect.KMutableProperty (kotlin.reflect.jvm.internal.KProperty1Impl and kotlin.reflect.KMutableProperty are in unnamed module of loader 'app')
at MainKt.main(main.kt:107)
at MainKt.main(main.kt)
since no setter code is generated for val
fields, and thus the info
property will have a Kotlin Reflection API type of KProperty
and not KMutableProperty
.
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