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


猜你喜欢
- 同步日志的业务流程处理和日志打印是在同一个线程,日志打印的过程实际上是写文件IO的过程,这个过程是相对耗时的,并且会阻塞主线程的执行,只有日
- 0.简介GridView 和 ListView 有共同的父类:AbsListView,因此 GridView 和 ListView 具有一定
- 本文实例为大家分享了C#实现拼手气红包算法的具体代码,供大家参考,具体内容如下一、方案1:即开即中,考虑机会均等,减少金额差较大的几率可以每
- 前言Spring 框架作为一个管理 Bean 的 IoC 容器,那么 Bean 自然是 Spring 中的重要资源了,那 Bean 的作用域
- 定义在类里面的类就叫做内部类。内部类的特点:在内部类中可以直接访问外部类的成员,包括私有的成员在外部类中不能直接访问内部类的成员,必须通过创
- C#操作Excel的方法有很多种,常见的有微软官方的OLE Automation,Apache的POI等。这里介绍的是POI翻译成C#的NP
- Lambda,希腊字母λ,在C#编程语言中,被引入为Lambda表达式,表示为匿名函数(匿名方法)。编程时离不开函数,
- createCoroutine 和 startCoroutine协程到底是怎么创建和启动的?本篇文章带你揭晓。在Continuation.k
- 前言Flutter 的画笔类 Paint 提供了很多图形绘制的配置属性,来供我们绘制更丰富多彩的图形。前面几篇我们介绍了 shader 属性
- 记录Java执行groovy脚本的两种方式,简单粗暴:一种是通过脚本引擎ScriptEngine提供的eval(String)方法执行脚本内
- 前言:前面几篇讲了自定义控件绘制原理Android自定义控件基本原理详解(一) ,Android自定义控件之自定义属性(二) ,Androi
- 相信大家对 String 和 StringBuffer 的区别也已经很了解了,但是估计还是会有很多同志对这两个类的工作原理有些不清楚的地方,
- 背景:SpringMVC如何响应json格式的数据?技术实现方式1:在Controller使用@RestController注解方式2:在C
- 一、前言前面我们讲了Java的入门知识,相信许多小伙伴对Java基础有一个大概的认识了,这也为我们后续的学习打下了基础,所以我们可以继续学习
- 本文实例讲述了C#从windows剪贴板获取并显示文本内容的方法。分享给大家供大家参考。具体如下:using System;using Sy
- 本文实例为大家分享了Java打印指定年月日历的具体代码,供大家参考,具体内容如下日历如下:程序如下://打印指定年月的日历public cl
- 本文实例为大家分享了Android实现表情功能的具体代码,供大家参考,具体内容如下Dialog实现表情评论功能核心问题:1、如何得到键盘弹起
- 在Android应用中,图片裁剪也是一个经常用到的功能。Android系统中可以用隐式意图调用系统应用进行裁剪,但是这样做在不同的手机可能表
- Android利用爬虫实现模拟登录的实现实例为了用手机登录校网时不用一遍一遍的输入账号密码,于是决定用爬虫抓取学校登录界面,然后模拟填写本次
- 弃用内容先来纠正一个误区。主要之前在版本更新介绍的时候,存在一些表述上的问题。导致部分读者认为这次的更新是Datasource本身初始化的调