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

所属分类: 软件编程 / Android 阅读数: 44
收藏 0 赞 0 分享

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

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

更多精彩内容其他人还在看

使用ViewPager实现android软件使用向导功能实现步骤

现在的大部分android软件,都是使用说明,就是第一次使用该软件时,会出现向导,可以左右滑动,然后就进入应用的主界面了,下面我们就实现这个功能
收藏 0 赞 0 分享

android在异步任务中关闭Cursor的代码方法

android在异步任务中如何关闭Cursor?在我们开发应用的时候,很多时候会遇到这种问题,下面我们就看看代码如何实现
收藏 0 赞 0 分享

Android自定义桌面功能代码实现

android自定义桌面其实很简单,看一个例子就明白了
收藏 0 赞 0 分享

android将图片转换存到数据库再从数据库读取转换成图片实现代码

有时候我们想把图片存入到数据库中,尽管这不是一种明智的选择,但有时候还是不得以会用到,下面说说将图片转换成byte[]数组存入到数据库中去,并从数据库中取出来转换成图像显示出来
收藏 0 赞 0 分享

TextView显示系统时间(时钟功能带秒针变化

用System.currentTimeMillis()可以获取系统当前的时间,我们可以开启一个线程,然后通过handler发消息,来实时的更新TextView上显示的系统时间,可以做一个时钟的功能
收藏 0 赞 0 分享

Android用ListView显示SDCard文件列表的小例子

本文简单实现了用ListView显示SDCard文件列表,目录的回退等功能暂不讨论,获取文件列表,files即为所选择目录下的所有文件列表
收藏 0 赞 0 分享

Android拦截外拨电话程序示例

这篇文章主要介绍了Android拦截外拨电话的示例,大家参考使用吧
收藏 0 赞 0 分享

通过Html网页调用本地安卓(android)app程序代码

如何使用html网页和本地app进行传递数据呢?经过研究,发现还是有方法的,总结了一下,大致有一下几种方式
收藏 0 赞 0 分享

android Textview文字监控(Textview使用方法)

以手机号充值为例,当用户输入最后一位数时候,进行汇率的变换,本文就实现类似这样的功能
收藏 0 赞 0 分享

Android ListView长按弹出菜单二种实现方式示例

这篇文章主要介绍了Android ListView长按弹出菜单的方法,大家参考实现
收藏 0 赞 0 分享
查看更多