我们可以依靠Gson修改val属性的能力吗?

时间:2019-02-02 08:22:21

标签: kotlin gson immutability

科特林的val属性在设计上是不可变的。它们应该是固定的,初始化后不能更改。

但是,我偶然发现Gson能够修改这些属性。

考虑以下示例:

//Entity class:
class User {
    val name: String = ""
    val age: Int = 0
}

由于nameage被定义为val并被初始化为空字符串和零,因此它们必须保持不变并且永远不变。

但是,此测试成功:

fun main() {
    val json = "{\"name\":\"Mousa\",\"age\":30}"
    val user = Gson().fromJson<User>(json, User::class.java)
    assert(user.name == "Mousa")
    assert(user.age == 30)
    print("Success")
}

使用此功能可使代码更清洁。因为这些属性是由Gson修改的,而其余代码无法修改。否则,我需要为Gson提供一些可变的支持字段,并为其余代码提供一些不可变的属性。

我不知道这是Gson的“功能”还是“错误”。所以我的问题是:我们可以依靠它吗,否则这种行为以后可能会改变?

1 个答案:

答案 0 :(得分:5)

如果运行javap -p User.class,则会得到以下输出:

public final class q54491286.User {
    private final java.lang.String name;
    private final int age;
    public final java.lang.String getName();
    public final int getAge();
    public q54491286.User();
}

Kotlin不变性不受为(隐式)声明为privatefinal的字段生成的任何变种器方法的支持。 除非您定义自定义类型适配器,否则Gson在设计上不会对纯数据对象类分别在序列化和反序列化期间by design中使用访问器或变量(请参见使用字段与getter中的更多here以指示Json元素部分)。 另外,Gson可以修改final字段,这也是一种设计选择。

由于大量使用反射,不变性在Java中是相对的一件事。 例如,我可以使用反射轻松修改不可变(通过设计)的字符串,使其具有某种可变性(通过intention):

final String s = "foo";
final Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set(s, new char[]{ 'b', 'a', 'r' });
System.out.println(s);

使用bar中的java时会产生java version "1.8.0_191"。 没有不变,呵呵。

另一方面,如果可以通过Gson类型适配器策略重定义方法以通用方式检测到您的类(Gson禁止重新定义ObjectJsonElement类型适配器),能够编写自定义反序列化程序,以禁止Kotlin生成的类(通过kotlin.Metadata注释检测)对只读字段进行反序列化(尽管不确定是否有好处)。 或者只是实现一个反序列化程序,该反序列化程序将扫描JSON流/树中的非只读字段,并仅在使用合法构造函数构造新的User对象时使用它们,而不使用反射。

  

我不知道这是Gson的“功能”还是Bug。

总结一下,这是一个功能。

  

所以我的问题是:我们可以依靠它吗,否则这种行为以后可能会改变?

这很可能在Gson 2.x中永远不会改变。 据我所知,没有任何计划发布主要更新Gson 3.x等。