kotlin gson反序列化默認值失效深入講解
Gson反序列化原理
原理簡述
gson反序列化主要分為兩個過程:
- 根據(jù)TypeToken創(chuàng)建出對象
- 根據(jù)json字符串解析數(shù)據(jù),對對象屬性賦值
對象的創(chuàng)建
ConstructorConstructor.get
- 先嘗試獲取無參構(gòu)造函數(shù)
- 失敗則嘗試List、Map等情況的構(gòu)造函數(shù)
- 最后使用Unsafe.newInstance兜底(此兜底不會調(diào)用構(gòu)造函數(shù),導(dǎo)致所有對象初始化代碼不會調(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ù),對于List,Map的情況 ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType); if (defaultImplementation != null) { return defaultImplementation; } // unSafe構(gòu)造出對象,不調(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)建出對象
- 不會調(diào)用構(gòu)造函數(shù),因此所有的初始化的代碼都不會被調(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òu)造函數(shù)
kotlin構(gòu)造函數(shù)默認參數(shù)和無參構(gòu)造函數(shù)的關(guān)系
參數(shù)里面存在沒有默認值的情況
kotlin代碼
- id沒有默認值
class User(val id: Int, val name: String = "sss") { init { println("init") } }
反編譯的Java代碼
- 包含兩個構(gòu)造函數(shù),一個是我們聲明的全參數(shù)構(gòu)造函數(shù),另一個是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實際結(jié)果是null)
null
Process finished with exit code 0
參數(shù)都包含默認參數(shù)的情況
kotlin代碼
class User(val id: Int=1, val name: String = "sss") { init { println("init") } }
反編譯Java代碼
- 除了上面的兩個構(gòu)造函數(shù),多了一個無參構(gòu)造函數(shù)(從邏輯上講,這個也符合預(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ù)都帶默認值
- 不確定的參數(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é)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Android 應(yīng)用中跳轉(zhuǎn)到應(yīng)用市場評分示例
本篇文章主要介紹了Android 應(yīng)用中跳轉(zhuǎn)到應(yīng)用市場評分示例,非常具有實用價值,需要的朋友可以參考下。2017-02-02Android操作SQLite數(shù)據(jù)庫(增、刪、改、查、分頁等)及ListView顯示數(shù)據(jù)的方法詳解
這篇文章主要介紹了Android操作SQLite數(shù)據(jù)庫(增、刪、改、查、分頁等)及ListView顯示數(shù)據(jù)的方法,結(jié)合實例形式詳細分析了Android操作SQLite數(shù)據(jù)庫及使用ListView顯示數(shù)據(jù)的相關(guān)技巧,需要的朋友可以參考下2016-02-02