Ответ
Короче говоря, вы должны использовать API отражения Java в этом случае, и вот как это сделать:
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)
}
Причина использования API отражения Java
Невозможно работать с API отражения Kotlin, поскольку для свойства только для чтения (val
) не генерируется установочный код. Таким образом, чтобы изменить его, нам нужно использовать API отражения Java, которые являются более низкоуровневыми. Сначала мы используем Инструменты -> 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
. Таким образом, чтобы изменить его, мы можем использовать API отражения Java, как в приведенном выше коде.
Попытка API отражения Kotlin
Если бы поле было private var
, вы могли бы использовать KotlinAPI отражения:
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)
, поскольку для полей val
не генерируется установочный код,и, таким образом, свойство info
будет иметь тип API отражения Kotlin KProperty
, а не KMutableProperty
.