JSON 序列化與反序列化(-)泛型 及 java.lang.reflect.Type
Type及其子接口的來歷
泛型出現之前的類型
沒有泛型的時候,只有原始類型。此時,所有的原始類型都通過字節碼文件類Class類進行抽象。Class類的一個具體對象就代表一個指定的原始類型。
泛型出現之後的類型
泛型出現之後,擴充了數據類型。從只有原始類型擴充了參數化類型、類型變量類型、限定符類型 、泛型數組類型。
與泛型有關的類型不能和原始類型統一到Class的原因
- 產生泛型擦除的原因
原始類型和新產生的類型都應該統一成各自的字節碼文件類型對象。但是由於泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集的修改,這是非常致命的。
- Java中如何引入泛型
為了使用泛型又不真正引入泛型,Java采用泛型擦除機制來引入泛型。Java中的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換的麻煩。但是,一旦編譯完成,所有的和泛型有關的類型全部擦除。
- Class不能表達與泛型有關的類型
因此,與泛型有關的參數化類型、類型變量類型、限定符類型 、泛型數組類型這些類型編譯後全部被打回原形,在字節碼文件中全部都是泛型被擦除後的原始類型,並不存在和自身類型對應的字節碼文件。所以和泛型相關的新擴充進來的類型不能被統一到Class類中。
- 與泛型有關的類型在Java中的表示
為了通過反射操作這些類型以迎合實際開發的需要,Java就新增了ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType
幾種類型來代表不能被歸一到Class類中的類型但是又和原始類型齊名的類型。
- 引入Type的原因
為了程序的擴展性,最終引入了Type接口作為Class和ParameterizedType, TypeVariable<D>, GenericArrayType, WildcardType
這幾種類型的總的父接口。這樣可以用Type類型的參數來接受以上五種子類的實參或者返回值類型就是Type類型的參數。統一了與泛型有關的類型和原始類型Class
- Type接口中沒有方法的原因
從上面看到,Type的出現僅僅起到了通過多態來達到程序擴展性提高的作用,沒有其他的作用。因此Type接口的源碼中沒有任何方法。
public static void getTypeParameters(){ List<Integer> list = new ArrayList<Integer>(); Map<Integer, String> map = new HashMap<Integer, String>(); System.out.println(Arrays.toString(list.getClass().getTypeParameters())); //[E] System.out.println(Arrays.toString(map.getClass().getTypeParameters())); //[K, V] } /** * 其中最關鍵的差別是本節的變量聲明多了一對大括號 * 變量聲明其實是創建了一個匿名內部類, 這個類是 HashMap 的子類 * * Java 引入泛型擦除的原因是避免因為引入泛型而導致運行時創建不必要的類。那我們其實就可以通過定義類的方式,在 * 類信息中保留泛型信息,從而在運行時獲得這些泛型信息 * * 簡而言之,Java 的泛型擦除是有範圍的,即類定義中的泛型是不會被擦除的 * */ public static void getActualTypeArguments(){ // Map<String, Integer> map = new HashMap<String, Integer>() ; Map<String, Integer> map = new HashMap<String, Integer>() {}; Type type = map.getClass().getGenericSuperclass(); ParameterizedType parameterizedType = ParameterizedType.class.cast(type); for (Type typeArgument : parameterizedType.getActualTypeArguments()) { System.out.println(typeArgument.getTypeName()); //java.lang.String, java.lang.Integer } }
參考:
Java的Type詳解
JSON 序列化與反序列化(-)泛型 及 java.lang.reflect.Type