Kotlin中Gson.fromJson()如何修改已有对象的只读val字段?实例解析
问题:Kotlin中只读val字段如何被修改?Gson.fromJson()是怎么实现修改已有对象的只读字段的?
我有一个疑问:在Kotlin中,实例的只读val字段怎么能被修改?特别是Gson的fromJson()方法,为什么能修改已有对象的只读字段?
比如下面这段代码,gson.fromJson()从InstanceCreator获取到Thing(0)实例,却能把它的value字段从0改成1,而且返回的还是同一个对象。我自己尝试用反射修改val字段但没成功,因为这个字段没有对应的setter方法,这到底是怎么做到的?
import com.google.gson.GsonBuilder import com.google.gson.InstanceCreator class Thing(val value: Int) fun main() { val creator = InstanceCreator { Thing(0) } val gson = GsonBuilder().registerTypeAdapter(Thing::class.java, creator).create() val thing = gson.fromJson("""{"value":1}""", Thing::class.java) println(thing.value) // 输出1 }
回答
1. Kotlin val字段在JVM上的本质
首先要明确:Kotlin的val关键字编译到JVM字节码后,会变成私有final字段加上对应的公共getter方法(比如getValue())。但这里的final只是编译器层面的限制,JVM本身并没有完全禁止通过反射修改final字段——只要你能绕过访问权限检查,就有可能修改它的值。
2. Gson反序列化的核心逻辑:直接操作字段(而非setter)
Gson在反序列化时,并不会依赖类的setter方法,它的核心思路是通过Java反射直接操作对象的底层字段,具体步骤如下:
- 当你通过
InstanceCreator提供了一个已有实例后,Gson会拿到这个对象的Class信息。 - 它会遍历JSON中的键值对,找到对应类的字段(比如这里的
value字段)。 - Gson会调用
Field.setAccessible(true),突破私有字段的访问限制。 - 对于final字段,Gson内部会通过反射修改字段的
modifiers属性,移除final修饰符(这一步是关键,绕过了JVM的final限制)。 - 最后直接调用
Field.set()方法,把JSON中的值赋值给这个字段。
这就是为什么你的代码中,InstanceCreator返回的Thing(0)实例,其value字段能被改成1——Gson完全绕开了Kotlin的只读限制,直接操作了底层的字段存储。
3. 你自己尝试反射失败的可能原因
如果你自己用反射没成功,大概率是没处理final字段的修饰符。举个简单的例子,手动实现类似Gson的逻辑:
import java.lang.reflect.Field import java.lang.reflect.Modifier fun main() { val thing = Thing(0) val field = Thing::class.java.getDeclaredField("value") // 第一步:突破私有访问限制 field.isAccessible = true // 第二步:移除final修饰符 val modifiersField = Field::class.java.getDeclaredField("modifiers") modifiersField.isAccessible = true modifiersField.setInt(field, field.modifiers and Modifier.FINAL.inv()) // 第三步:直接赋值 field.set(thing, 1) println(thing.value) // 输出1 }
这段代码就能成功修改val字段的值,核心就是移除了字段的final修饰符——这也是Gson内部在做的事情。
内容的提问来源于stack exchange,提问作者tashburn
相关产品推荐
相关产品推荐

