欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-04-12 11:42:54
gson反序列化原理 原理简述 gson反序列化主要分为两个过程: 根据typetoken创建出对象 根据json字符串解析数据,对对象属性赋值...

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")
 }
}

总结

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