kotlin gson反序列化默认值失效深入讲解

Gson反序列化原理

原理简述

gson反序列化主要分为两个过程:

  • 根据TypeToken创建出对象
  • 根据json字符串解析数据,对对象属性赋值

对象的创建

ConstructorConstructor.get

  • 先尝试获取无参构造函数
  • 失败则尝试List、Map等情况的构造函数
  • 最后使用Unsafe.newInstance兜底(此兜底不会调用构造函数,导致所有对象初始化代码不会调用)
public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
 final Type type = typeToken.getType();
 final Class<? super T> rawType = typeToken.getRawType();

 // first try an instance creator

 @SuppressWarnings("unchecked") // types must agree
 final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
 if (typeCreator != null) {
  return new ObjectConstructor<T>() {
  @Override public T construct() {
   return typeCreator.createInstance(type);
  }
  };
 }

 // Next try raw type match for instance creators
 @SuppressWarnings("unchecked") // types must agree
 final InstanceCreator<T> rawTypeCreator =
  (InstanceCreator<T>) instanceCreators.get(rawType);
 if (rawTypeCreator != null) {
  return new ObjectConstructor<T>() {
  @Override public T construct() {
   return rawTypeCreator.createInstance(type);
  }
  };
 }
 // 获取无参构造函数
 ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
 if (defaultConstructor != null) {
  return defaultConstructor;
 }

 // 获取List<T>,Map<T>等构造函数,对于List,Map的情况
 ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
 if (defaultImplementation != null) {
  return defaultImplementation;
 }

 // unSafe构造出对象,不调用任何的构造函数
 // finally try unsafe
 return newUnsafeAllocator(type, rawType);
 }

ConstructorConstructor.newDefaultConstructor

  • 调用Class.getDeclaredConstructor获取无参构造函数
private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
 try {
  // 获取无参构造函数
  final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
  if (!constructor.isAccessible()) {
  accessor.makeAccessible(constructor);
  }

ConstructorConstructor.newUnsafeAllocator

  • 调用UnSafe.newInstance创建出对象
  • 不会调用构造函数,因此所有的初始化的代码都不会被调用
private <T> ObjectConstructor<T> newUnsafeAllocator(
  final Type type, final Class<? super T> rawType) {
 return new ObjectConstructor<T>() {
  private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
  @SuppressWarnings("unchecked")
  @Override public T construct() {
  try {
  //
   Object newInstance = unsafeAllocator.newInstance(rawType);
   return (T) newInstance;
  } catch (Exception e) {
   throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". "
    + "Registering an InstanceCreator with Gson for this type may fix this problem."), e);
  }
  }
 };
 }

结论

  • Gson反序列要工作正常,使结果符合预期的话,要求类必须有一个无参构造函数

kotlin构造函数默认参数和无参构造函数的关系

参数里面存在没有默认值的情况

kotlin代码

  • id没有默认值
class User(val id: Int, val name: String = "sss") {
 init {
  println("init")
 }
}

反编译的Java代码

  • 包含两个构造函数,一个是我们声明的全参数构造函数,另一个是kotlin生成的辅助构造函数
  • 不包含无参构造函数
public final class User {
 private final int id;
 @NotNull
 private final String name;

 public User(int id, @NotNull String name) {
  Intrinsics.checkParameterIsNotNull(name, "name");
  super();
  this.id = id;
  this.name = name;
  String var3 = "init";
  System.out.println(var3);
 }

 // $FF: synthetic method
 public User(int var1, String var2, int var3, DefaultConstructorMarker var4) {
  if ((var3 & 2) != 0) {
   var2 = "";
  }

  this(var1, var2);
 }
}

gson反序列化输出

代码:

 @Test
 fun testJson() {
  val user = Gson().fromJson("{}", User::class.java)
  print(user.name)
 }

输出:不符合预期(我们声明的非空的name实际结果是null)

null
Process finished with exit code 0

参数都包含默认参数的情况

kotlin代码

class User(val id: Int=1, val name: String = "sss") {
 init {
  println("init")
 }
}

反编译Java代码

  • 除了上面的两个构造函数,多了一个无参构造函数(从逻辑上讲,这个也符合预期)
public final class User {
 private final int id;
 @NotNull
 private final String name;

 public User(int id, @NotNull String name) {
  Intrinsics.checkParameterIsNotNull(name, "name");
  super();
  this.id = id;
  this.name = name;
  String var3 = "init";
  System.out.println(var3);
 }

 // $FF: synthetic method
 public User(int var1, String var2, int var3, DefaultConstructorMarker var4) {
  if ((var3 & 1) != 0) {
   var1 = 1;
  }

  if ((var3 & 2) != 0) {
   var2 = "";
  }

  this(var1, var2);
 }

 // 无参构造函数
 public User() {
  this(0, (String)null, 3, (DefaultConstructorMarker)null);
 }
}

gson反序列化输出

代码:

 @Test
 fun testJson() {
  val user = Gson().fromJson("{}", User::class.java)
  print(user.name)
 }

输出:符合预期

init
sss
Process finished with exit code 0

Best Practice

Practice1

  • 属性声明在构造函数,所有参数都带默认值
  • 不确定的参数声明为可空
class User(val id: Int=1 , val name: String = "sss") {
 init {
  println("init")
 }
}

Practice2

回归到Java的写法即可

class User {
 val id: Int = 1
 val name: String = "sss"

 init {
  println("init")
 }
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • GSON实现Java对象的JSON序列化与反序列化的实例教程

    从GitHub下载GSON:https://github.com/google/gson Gson的应用主要为toJson与fromJson两个转换函数,而在使用这种对象转换之前需先创建好对象的类别以及其成员才能成功的将JSON字符串成功转换成相对应的对象. class Examples { private int answer1 = 100; private String answer2 = "Hello world!"; Examples(){ } // default const

  • kotlin gson反序列化默认值失效深入讲解

    Gson反序列化原理 原理简述 gson反序列化主要分为两个过程: 根据TypeToken创建出对象 根据json字符串解析数据,对对象属性赋值 对象的创建 ConstructorConstructor.get 先尝试获取无参构造函数 失败则尝试List.Map等情况的构造函数 最后使用Unsafe.newInstance兜底(此兜底不会调用构造函数,导致所有对象初始化代码不会调用) public <T> ObjectConstructor<T> get(TypeToken<

  • python函数指定默认值的实例讲解

    1.说明 (1)写函数时,可以为每个参数指定默认值.当调用函数为参数提供实际参数时,Python将使用指定的实际参数:否则,将使用参数的默认值.因此,给参数指定默认值后,可以在函数调用中省略相应的参数. (2)使用默认值可以简化函数调用,明确指出函数的典型用法. 2.实例 >>> def student(name, age=18): ... print('Hello, My name is ' + name + ', I am ' + str(age) + ' years old') .

  • Kotlin函数默认值的完全讲解

    函数默认值 周所周知,Java语言并不支持参数使用默认值.有人说这是因为"默认参数"和"方法重载"同时支持的话有二义性的问题,具体真正的原因我不得而知.但是对我个人来说,Java不支持这个特性的确挺让我蛋疼的,虽然说使用方法重载也可以间接实现与默认参数这个特性相同的功能,但这就意味着你得写更多的代码-- 简要介绍 Kotlin函数定义时,支持对参数指定默认值,这样就有效减少Java之前定义重载函数的数量. 简要对比如下: 1.Java函数定义,如果sayHelloT

  • 解决antd 表单设置默认值initialValue后验证失效的问题

    方法一: getFieldDecorator没有第三个参数,如果写了3个参数就会出错 错误代码: <Form.Item> { getFieldDecorator('userName', { initialValue: 'Tom' },{ rules: [{ required: true, message: '请输入用户名', }], })( <Input placeholder='请输入用户名'/> ) } </Form.Item> 正确代码: <Form.Ite

  • C++超详细讲解函数参数的默认值

    目录 1.参数默认值的指定方式 2.参数默认值的指定规则 1.参数默认值的指定方式 在 C++ 中,可以在函数声明时为参数提供一个默认值.这样在函数调用时,如果没有提供函数参数值,则使用默认值. e.g. 在函数声明时,指定参数默认值 void Demo(int x = 0); int main(int argc, char* argv[]) { Demo(); // 不用提供参数,会将 x = 0 的结果输出 return 0; } void Demo(int x) { printf("x =

  • innodb_flush_method取值方法(实例讲解)

    innodb_flush_method的几个典型取值 fsync: InnoDB uses the fsync() system call to flush both the data and log files. fsync is the default setting. O_DSYNC: InnoDB uses O_SYNC to open and flush the log files, and fsync() to flush the data files. InnoDB does no

  • 关于 文本框默认值 的操作js代码

    比如: 搜索框:可以 <input value="搜索产品..." /> 文本区域: 可以 <textarea>输入您多我的建议或者意见,让我们的网站为您创造更便捷的服务....</textarea> ........ 首先说这种提示功能很好,非常直观,可是怎么让它们获取焦点时内容清空.失去焦点后内容分为空的话就显示提示文本..... 当页面这些元素多了之后如何去管理...等等问题 复制代码 代码如下: var inputText=function(

  • MySQL为何不建议使用默认值为null列

    通常能听到的答案是使用了NULL值的列将会使索引失效,但是如果实际测试过一下,你就知道IS NULL会使用索引.所以上述说法有漏洞. 着急的人拉到最下边看结论 Preface Null is a special constraint of columns. The columns in table will be added null constrain if you do not define the column with "not null" key words explicit

  • mysql回表致索引失效案例讲解

    简介 mysql的innodb引擎查询记录时在无法使用索引覆盖的场景下,需要做回表操作获取记录的所需字段. mysql执行sql前会执行sql优化.索引选择等操作,mysql会预估各个索引所需要的查询代价以及不走索引所需要的查询代价,从中选择一个mysql认为代价最小的方式进行sql查询操作.而在回表数据量比较大时,经常会出现mysql对回表操作查询代价预估代价过大而导致索引使用错误的情况. 案例 示例如下,在5.6版本的mysql.1CPU2G内存的Linux环境下,新建一个测试表,并创建将近

  • 给JavaBean赋默认值并且转Json字符串的实例

    目录 JavaBean赋默认值并转Json字符串 此段代码还有值得优化的地方 javaBean对象和Json格式字符串相互转换 一.使用Jackson工具类 二.使用Gson工具类 JavaBean赋默认值并转Json字符串 今日在工作中整理dubbo接口的时候,有些接口参数类型是JavaBean类型的数据,在整理的时候又想直接将JavaBean参数直接以默认的json格式整理,于是想到如何将javaBean的默认值 直接转换成为 Json格式的数据,避免自己手动去拼写json字符串. 此段代码

随机推荐