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

作者:寻找极限的猫 时间:2022-04-07 15:28:59 

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

来源:https://juejin.im/post/5c1b1d346fb9a049d61d7dcc

标签:kotlin,gson,反序列化
0
投稿

猜你喜欢

  • android 多点触摸图片缩放的具体实现方法

    2023-03-14 05:49:31
  • springboot之如何获取项目目录路径

    2022-08-09 21:44:17
  • Java为什么占用四个字节你知道吗

    2021-06-16 18:05:22
  • 解决使用RestTemplate时报错RestClientException的问题

    2023-05-27 19:46:36
  • springboot+mybatis配置控制台打印sql日志的方法

    2023-12-15 15:40:55
  • 构建多模块的Spring Boot项目步骤全纪录

    2022-02-20 04:28:44
  • java后台接收app上传的图片的示例代码

    2022-11-03 00:04:15
  • Android开发使用URLConnection进行网络编程详解

    2022-01-07 22:49:28
  • java实现LRU缓存淘汰算法的方法

    2022-10-09 00:09:05
  • Android跳转到通讯录获取用户名称和手机号码的实现思路

    2023-12-11 16:42:57
  • java 创建线程的四种方式

    2023-11-02 21:38:07
  • Android中获取资源 id 及资源 id 的动态获取

    2023-06-30 04:38:06
  • C# URL短地址压缩算法及短网址原理解析

    2023-03-26 08:36:31
  • Netty分布式FastThreadLocal的set方法实现逻辑剖析

    2021-08-22 04:51:54
  • 怎么把本地jar包放入本地maven仓库和远程私服仓库

    2023-12-05 20:13:00
  • java多线程之线程同步七种方式代码示例

    2022-04-29 17:55:46
  • IntelliJ IDEA maven 构建简单springmvc项目(图文教程)

    2021-09-13 05:40:40
  • Java Spring开发环境搭建及简单入门示例教程

    2021-08-17 07:36:28
  • mybatis 查询返回Map<String,Object>类型

    2023-11-14 07:06:09
  • Android开发注解排列组合出启动任务ksp

    2022-10-06 21:25:16
  • asp之家 软件编程 m.aspxhome.com