kotlin gson反序列化默認(rèn)值失效深入講解
Gson反序列化原理
原理簡(jiǎn)述
gson反序列化主要分為兩個(gè)過程:
- 根據(jù)TypeToken創(chuàng)建出對(duì)象
- 根據(jù)json字符串解析數(shù)據(jù),對(duì)對(duì)象屬性賦值
對(duì)象的創(chuàng)建
ConstructorConstructor.get
- 先嘗試獲取無參構(gòu)造函數(shù)
- 失敗則嘗試List、Map等情況的構(gòu)造函數(shù)
- 最后使用Unsafe.newInstance兜底(此兜底不會(huì)調(diào)用構(gòu)造函數(shù),導(dǎo)致所有對(duì)象初始化代碼不會(huì)調(diào)用)
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);
}
};
}
// 獲取無參構(gòu)造函數(shù)
ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
if (defaultConstructor != null) {
return defaultConstructor;
}
// 獲取List<T>,Map<T>等構(gòu)造函數(shù),對(duì)于List,Map的情況
ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
if (defaultImplementation != null) {
return defaultImplementation;
}
// unSafe構(gòu)造出對(duì)象,不調(diào)用任何的構(gòu)造函數(shù)
// finally try unsafe
return newUnsafeAllocator(type, rawType);
}
ConstructorConstructor.newDefaultConstructor
- 調(diào)用Class.getDeclaredConstructor獲取無參構(gòu)造函數(shù)
private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
try {
// 獲取無參構(gòu)造函數(shù)
final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
if (!constructor.isAccessible()) {
accessor.makeAccessible(constructor);
}
ConstructorConstructor.newUnsafeAllocator
- 調(diào)用UnSafe.newInstance創(chuàng)建出對(duì)象
- 不會(huì)調(diào)用構(gòu)造函數(shù),因此所有的初始化的代碼都不會(huì)被調(diào)用
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);
}
}
};
}
結(jié)論
- Gson反序列要工作正常,使結(jié)果符合預(yù)期的話,要求類必須有一個(gè)無參構(gòu)造函數(shù)
kotlin構(gòu)造函數(shù)默認(rèn)參數(shù)和無參構(gòu)造函數(shù)的關(guān)系
參數(shù)里面存在沒有默認(rèn)值的情況
kotlin代碼
- id沒有默認(rèn)值
class User(val id: Int, val name: String = "sss") {
init {
println("init")
}
}
反編譯的Java代碼
- 包含兩個(gè)構(gòu)造函數(shù),一個(gè)是我們聲明的全參數(shù)構(gòu)造函數(shù),另一個(gè)是kotlin生成的輔助構(gòu)造函數(shù)
- 不包含無參構(gòu)造函數(shù)
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)
}
輸出:不符合預(yù)期(我們聲明的非空的name實(shí)際結(jié)果是null)
null
Process finished with exit code 0
參數(shù)都包含默認(rèn)參數(shù)的情況
kotlin代碼
class User(val id: Int=1, val name: String = "sss") {
init {
println("init")
}
}
反編譯Java代碼
- 除了上面的兩個(gè)構(gòu)造函數(shù),多了一個(gè)無參構(gòu)造函數(shù)(從邏輯上講,這個(gè)也符合預(yù)期)
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);
}
// 無參構(gòu)造函數(shù)
public User() {
this(0, (String)null, 3, (DefaultConstructorMarker)null);
}
}
gson反序列化輸出
代碼:
@Test
fun testJson() {
val user = Gson().fromJson("{}", User::class.java)
print(user.name)
}
輸出:符合預(yù)期
init
sss
Process finished with exit code 0
Best Practice
Practice1
- 屬性聲明在構(gòu)造函數(shù),所有參數(shù)都帶默認(rèn)值
- 不確定的參數(shù)聲明為可空
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")
}
}
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android 使用Retrofit 以純二進(jìn)制文件流上傳文件的操作代碼
文章介紹了如何在Android項(xiàng)目中使用Retrofit通過純二進(jìn)制文件流上傳文件,包括單個(gè)文件流上傳和大文件分段上傳的方法,并詳細(xì)描述了需求協(xié)議、接口定義、RequestInterceptor的使用以及相關(guān)庫(kù)的調(diào)用,感興趣的朋友跟隨小編一起看看吧2024-11-11
Android Canvas的drawText()與文字居中方案詳解
這篇文章主要給大家介紹了關(guān)于Android Canvas的drawText()與文字居中方案的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
Android中使用七牛云存儲(chǔ)進(jìn)行圖片上傳下載的實(shí)例代碼
這篇文章主要介紹了Android中使用七牛云存儲(chǔ)進(jìn)行圖片上傳下載的實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-08-08
Android實(shí)現(xiàn)pdf在線預(yù)覽或本地預(yù)覽的方法
下面小編就為大家分享一篇Android實(shí)現(xiàn)pdf在線預(yù)覽或本地預(yù)覽的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01
Android開發(fā)中使用Volley庫(kù)發(fā)送HTTP請(qǐng)求的實(shí)例教程
這篇文章主要介紹了Android開發(fā)中使用Volley庫(kù)發(fā)送HTTP請(qǐng)求的實(shí)例教程,包括創(chuàng)建Volley單例的基本知識(shí)與取消Request請(qǐng)求的技巧等,需要的朋友可以參考下2016-05-05
Moshi?完美解決Gson在kotlin中默認(rèn)值空的問題詳解
這篇文章主要為大家介紹了Moshi?完美解決Gson在kotlin中默認(rèn)值空的問題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
android開發(fā)實(shí)現(xiàn)列表控件滾動(dòng)位置精確保存和恢復(fù)的方法(推薦)
下面小編就為大家?guī)硪黄猘ndroid開發(fā)實(shí)現(xiàn)列表控件滾動(dòng)位置精確保存和恢復(fù)的方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03

