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
猜你喜欢
- 平时用到的库仓库名地址备注mavenCentralhttps://repo1.maven.org/maven2/
- 本文实例讲述了Java使用Math.random()结合蒙特卡洛方法计算pi值。分享给大家供大家参考,具体如下:一、概述蒙特·卡罗方法(Mo
- 最近重构了一下我的存档框架。我在这里对实现方法进行简单的解析。注意这里主要演示算法,所以,效率上并不是最佳。一个游戏中,可能有成百上千个物体
- 1.添加引用Windows服务(.NET Framework)2.输入项目名称,选择安装位置,,选择安装框架版本;创建。3.找到MyServ
- 本文介绍了Android Studio 超级简单的打包生成apk,分享给大家,也给自己留个笔记。为什么要打包:apk文件就是一个包,打包就是
- 这篇文章主要介绍了SpringBoot Jpa分页查询配置方式解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价
- 概要设计模式是一门艺术,如果真正了解这门艺术,你会发现,世界都将变得更加优美。定义定义一个用于创建对象的接口,让其子类去决定实例化那个类使用
- 流程控制语句是C语言中最基本的判断语句,通常我们可以使用IF来构建多分支结构,但同样可以使用Switch语句构建,Switch语句针对多分支
- 在SpringMVC的入门学习中,我发现@GetMapping注解的使用要注意路径冲突问题,在网上都没找到类似我这样的情况,所以我在这里将问
- 前言可能会涉及到汇编的知识,不过这没有关系,肯定能看懂,看不懂留言,我再做解释。使用到的工具是vs2010。本节只讲继承的特点,公有私有多态
- 如图,左图是效果,右图是原理,右图X轴代表图像一个像素点的灰度,Y轴代表RGB三个颜色对应的伪彩 * 颜色。代码如下:for (int y =
- 本文实例为大家分享了Android实现仿网易音乐唱片播放效果的具体代码,供大家参考,具体内容如下效果图: 在values中创建attrs.x
- 1、前言在日常的Spring Boot项目开发中,我们都会建立几个固有的包,分别是Controller、entity(pojo)、dao、s
- 后台服务端import java.io.IOException;import java.io.InputStream;import java
- 目前为止,我遇到使用Tomcat有三种情况:第一,使用Eclipse,在Eclipse中配置Tomcat。第二,直接在Tomcat中部署项目
- 前言MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以
- Runtime.getRuntime().exec 路径包含空格1. 现象java代码通过Runtime.getRuntime().exec
- 属性(Attribute)是C#程序设计中非常重要的一个技术,应用范围广泛,用法灵活多变。本文就以实例形式分析了C#中属性的应用。具体入戏:
- 重新认识 Java 的 System.in以前也写过不少命令行的程序,处理文件时总需要通过参数指定路径,直到今天看资料时发现了一种我自己从来
- 目录1、java有8种基本类型,请问byte、int、long、char、float、double、boolean各占多少个字节?2、在 A