我正在尝试使用反射来设置 Kotlin 对象的 val 属性。当我尝试下面的代码时,出现异常:
无法将静态最终 Foo 字段 Person.foo 设置为 Biz java.lang.IllegalAccessException:无法将静态最终 Foo 字段 Person.foo 设置为 Biz
interface Foo {
fun hello(): String
}
class Bar: Foo {
override fun hello(): String {
return "Bar"
}
}
class Biz: Foo {
override fun hello(): String {
return "Biz"
}
}
object Person {
val foo: Foo = Bar()
}
fun main() {
val person = Person
val property = person::class.memberProperties.first { it.name == "foo" }
val javaField = property.javaField!!
javaField.isAccessible = true
javaField.set(person, Biz()) // exception happens here
println(Person.foo.hello())
}
有没有办法让 Person.foo.hello() 通过 Kotlin 或 Java 反射 API 返回“Biz”而不是“Bar”?
谢谢!
不幸的是,
val
中的object
的支持字段被编译为类文件中的static final
字段。这些字段“非常”难以改变。请参阅 this post 了解在 Java 中执行此操作的方法。对于每个 JVM 版本,这变得越来越难,而且我还没有找到适用于 Java 22 的解决方案。
这里是来自链接问题的这个答案的 Kotlin 翻译。这在 Java 16 之前都有效。
fun main() {
val person = Person
val property = person::class.memberProperties.first { it.name == "foo" }
val javaField = property.javaField!!
javaField.setFieldAccessible()
javaField.set(person, Biz()) // or javaField.set(null, Biz()), since the field is static
println(Person.foo.hello())
}
fun Field.setFieldAccessible() {
setAccessible(true)
val lookup = MethodHandles.privateLookupIn(Field::class.java, MethodHandles.lookup())
val modifiersHandle = lookup.findVarHandle(Field::class.java, "modifiers", Int::class.javaPrimitiveType)
val mods: Int = modifiers
if (Modifier.isFinal(mods)) {
modifiersHandle.set(this, mods and Modifier.FINAL.inv())
}
}
fun main() {
val unsafeField = Unsafe::class.java.getDeclaredField("theUnsafe")
unsafeField.isAccessible = true
val unsafe = unsafeField.get(null) as Unsafe
val field = Person.javaClass.getDeclaredField("foo") // <-- FIELD WE WANT TO CHANGE
val base = unsafe.staticFieldBase(field)
val offset = unsafe.staticFieldOffset(field)
unsafe.putObject(base, offset, Biz()) // <-- REPLACE
println(Person.foo.hello())
}