在使用fastJson的時候對于泛型的反序列化很多場景下都會使用到TypeReference,例如:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
JSONObject o = new JSONObject();
o.put("k",list);
List<String> types = o.getObject("k",List.class);
System.out.println(JSON.toJSONString(types));
List<String> types2 = o.getObject("k",new TypeReference<List<String>>(){});
System.out.println(JSON.toJSONString(types2));
}
使用TypeReference可以明确的指定反序列化的類型,具體實作邏輯參考TypeReference的構造函數
protected TypeReference(){
Type superClass = getClass().getGenericSuperclass();
Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
Type cachedType = classTypeCache.get(type);
if (cachedType == null) {
classTypeCache.putIfAbsent(type, type);
cachedType = classTypeCache.get(type);
}
this.type = cachedType;
}
核心的方法是
getActualTypeArguments
,此方法可以擷取父類真實的泛型類型。具體參考注釋
java.lang.Class#getGenericSuperclass
。
new TypeReference<List<String>>(){}
建立了一個繼承TypeReference>的匿名子類,在其構造函數中拿到了泛型對應Type(
java.lang.reflect.ParameterizedType
)。
ParameterizedType是一個記錄類型泛型的接口, 繼承自Type, 一共三方法:
- Type[] getActualTypeArguments(); //傳回泛型類型數組
- Type getRawType(); //傳回原始類型Type
- Type getOwnerType(); //傳回 Type 對象,表示此類型是其成員之一的類型。
例如
Map<String,String>
對應的ParameterizedType三個方法分别取值如下:
- [class java.lang.String, class java.lang.String]
- interface java.util.Map
- null
TypeReference
的存在是因為java中子類可以擷取到父類泛型的真實類型,為了便于了解,看一段測試代碼
public class TypeReferenceKest {
public static void main(String[] args) {
IntMap intMap = new IntMap();
System.out.println(intMap.getClass().getSuperclass());
Type type = intMap.getClass().getGenericSuperclass();
if(type instanceof ParameterizedType){
ParameterizedType p = (ParameterizedType) type;
for (Type t : p.getActualTypeArguments()){
System.out.println(t);
}
}
System.out.println("=====newclass=====");
HashMap<String,Integer> newIntMap = new HashMap<>();
System.out.println(newIntMap.getClass().getSuperclass());
Type newClassType = newIntMap.getClass().getGenericSuperclass();
if(newClassType instanceof ParameterizedType){
ParameterizedType p = (ParameterizedType) newClassType;
for (Type t : p.getActualTypeArguments()){
System.out.println(t);
}
}
System.out.println("=====subclass=====");
HashMap<String,Integer> subIntMap = new HashMap<String,Integer>(){};
System.out.println(subIntMap.getClass().getSuperclass());
Type subClassType = subIntMap.getClass().getGenericSuperclass();
if(subClassType instanceof ParameterizedType){
ParameterizedType p = (ParameterizedType) subClassType;
for (Type t : p.getActualTypeArguments()){
System.out.println(t);
}
}
}
public static class IntMap extends HashMap<String,Integer> {
}
}
輸出為
class java.util.HashMap
class java.lang.String
class java.lang.Integer
=====newclass=====
class java.util.AbstractMap
K
V
=====subclass=====
class java.util.HashMap
class java.lang.String
class java.lang.Integer
擷取到了真實的類型,就可以實作對泛型的反序列化了。
參考資料
http://www.java2s.com/Tutorials/Java/java.lang/Class/Java_Class_getGenericSuperclass_.htm https://zhaoyanblog.com/archives/186.htmlps.
java雖然運作時會有類型擦除,但是會保留Field的泛型資訊,可以通過Field.getGenericType() 取字段的泛型。
exp
public class FieldGenericKest {
public Map<String,Integer> map = new HashMap<>();
public List<Long> list = new ArrayList<>();
public static void main(String[] args) throws Exception {
FieldGenericKest kest = new FieldGenericKest();
Field map = kest.getClass().getField("map");
Field list = kest.getClass().getField("list");
System.out.println("=====map=====");
System.out.println("map.getType=" + map.getType());
System.out.println("map.getGenericType=" + map.getGenericType());
System.out.println("=====list=====");
System.out.println("list.getType=" + list.getType());
System.out.println("list.getGenericType=" + list.getGenericType());
}
}
輸出
=====map=====
map.getType=interface java.util.Map
map.getGenericType=java.util.Map<java.lang.String, java.lang.Integer>
=====list=====
list.getType=interface java.util.List
list.getGenericType=java.util.List<java.lang.Long>
但是注意,這裡不能擷取到字段的真實類型
HashMap
和
ArrayList
真實的類型當然不能用Field來擷取,需要用對應的Value來擷取
Object mapVal = map.get(kest);
if(mapVal != null){
Class<?> clz = mapVal.getClass();
System.out.println(mapVal.getClass().getName());
}
pps.
因為泛型的運作時擦除,對于局部變量來說, 泛型資訊是無法擷取的