Answer
简而言之,在这种情况下您必须使用 Java 反射 API,具体操作方法如下:
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)
}
使用 Java 反射 API 的原因
无法使用 Kotlin 反射 API 执行此操作,因为不会为只读生成 setter 代码 (val
) 财产。因此,要改变它,我们需要使用更底层的Java反射API。首先,我们使用Tools -> Kotlin -> 显示 Kotlin 字节码查看生成的字节码是什么样子的。然后我们看到这个:
// ================MainClass.class =================
// class version 50.0 (50)
// access flags 0x31
public final class MainClass {
// access flags 0x12
private final Ljava/lang/String; info = "Hello"
// ...
即info
中的字段MainClass
Kotlin 类会导致编译器发出常规的 JVM 代码MainClass
Java 类有一个final String info
场地。因此,要更改它,我们可以使用 Java 反射 API,如上面的代码所示。
Kotlin 反射 API 尝试
如果该字段是private var
您将能够像这样使用 Kotlin 反射 API:
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)
}
但如果你尝试这样做private val
你会得到以下异常
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)
因为没有生成 setter 代码val
场,因此info
属性将具有 Kotlin Reflection API 类型KProperty
并不是KMutableProperty
.