kotlin gson反序列化默認值失效

Gson反序列化原理

原理簡述

gson反序列化主要分爲兩個過程:java

  1. 根據TypeToken建立出對象
  2. 根據json字符串解析數據,對對象屬性賦值

對象的建立

ConstructorConstructor.getjson

  • 先嚐試獲取無參構造函數
  • 失敗則嘗試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.newDefaultConstructorbash

  • 調用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.newUnsafeAllocatoride

  • 調用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)ui

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反序列化輸出

代碼:this

@Test
    fun testJson() {
        val user = Gson().fromJson("{}", User::class.java)
        print(user.name)
    }
複製代碼

輸出:符合預期spa

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")
    }
}
複製代碼
相關文章
相關標籤/搜索