您好,登錄后才能下訂單哦!
Java中TypeToken的原理是什么,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
泛型擦除
眾所周知,Java的泛型只在編譯時有效,到了運行時這個泛型類型就會被擦除掉,即List<String>和List<Integer>在運行時其實都是List<Object>類型。
為什么選擇這種實現機制?不擦除不行么?在Java誕生10年后,才想實現類似于C++模板的概念,即泛型。Java的類庫是Java生態中非常寶貴的財富,必須保證向后兼容(即現有的代碼和類文件依舊合法)和遷移兼容(泛化的代碼和非泛化的代碼可互相調用)基于上面這兩個背景和考慮,Java設計者采取了“類型擦除”這種折中的實現方式。
同時正正有這個這么“坑”的機制,令到我們無法在運行期間隨心所欲的獲取到泛型參數的具體類型。
TypeToken
使用
使用過Gson的同學都知道在反序列化時需要定義一個TypeToken類型,像這樣
private Type type = new TypeToken<List<Map<String, Foo>>>(){}.getType(); //調用fromJson方法時把type傳過去,如果type的類型和json保持一致,則可以反序列化出來 gson.fromJson(json, type);
三個問題
1.為什么要用TypeToken來定義反序列化的類型?正如上面說的,如果直接把List<Map<String, Foo>>的類型傳過去,但是因為運行時泛型被擦除了,所以得到的其實是List<Object>,那么后面的Gson就不知道要轉成Map<String, Foo>類型了,這時Gson會默認轉成LinkedTreeMap類型。
2.為什么帶有大括號{}?這個大括號就是精髓所在。大家都知道,在Java語法中,在這個語境,{}是用來定義匿名類,這個匿名類是繼承了TypeToken類,它是TypeToken的子類。
3.為什么要通過子類來獲取泛型的類型?這是TypeToken能夠獲取到泛型類型的關鍵,這是一個巧妙的方法。這個想法是這樣子的,既然像List<String>這樣中的泛型會被擦除掉,那么我用一個子類SubList extends List<String>這樣的話,在JVM內部中會不會把父類泛型的類型給保存下來呢?
我這個子類需要繼承的父類的泛型都是已經確定了的呀,果然,JVM是有保存這部分信息的,它是保存在子類的Class信息中。
具體看:
https://stackoverflow.com/questions/937933/where-are-generic-types-stored-in-java-class-files
那么我們怎么獲取這部分信息呢?還好,Java有提供API出來:
Type mySuperClass = foo.getClass().getGenericSuperclass(); Type type = ((ParameterizedType)mySuperClass).getActualTypeArguments()[0]; System.out.println(type);
分析一下這段代碼,Class類的getGenericSuperClass()方法的注釋是:
Returns the Type representing the direct superclass of the entity (class, interface, primitive type or void) represented by thisClass. If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code. The parameterized type representing the superclass is created if it had not been created before. See the declaration of ParameterizedType for the semantics of the creation process for parameterized types. If thisClass represents either theObject class, an interface, a primitive type, or void, then null is returned. If this object represents an array class then theClass object representing theObject class is returned
概括來說就是對于帶有泛型的class,返回一個ParameterizedType對象,對于Object、接口和原始類型返回null,對于數 組class則是返回Object.class。ParameterizedType是表示帶有泛型參數的類型的Java類型,JDK1.5引入了泛型之 后,Java中所有的Class都實現了Type接口,ParameterizedType則是繼承了Type接口,所有包含泛型的Class類都會實現 這個接口。
自己調試一下就知道它返回的是什么了。
原理
核心的方法就是剛剛說的那兩句,剩下的就很簡單了。我們看看TypeToken的getType方法
public final Type getType() { //直接返回type return type; }
看type的初始化
//注意這里用了protected關鍵字,限制了只有子類才能訪問 protected TypeToken() { this.type = getSuperclassTypeParameter(getClass()); this.rawType = (Class<? super T>) $Gson$Types.getRawType(type); this.hashCode = type.hashCode(); } //getSuperclassTypeParameter方法 //這幾句就是上面的說到 static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; //這里注意一下,返回的是Gson自定義的,在$Gson$Types里面定義的TypeImpl等,這個類都是繼承Type的。 return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); }
關于Java中TypeToken的原理是什么問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。