一、使用泛型的好處
1.泛型確定了具體的數(shù)據(jù)類型,對于List,Map這樣的數(shù)據(jù)容器提供了類型檢測機(jī)制,只有相匹配的數(shù)據(jù)才能正常的賦值,否則編譯器就不通過,它是一種類型安全檢測機(jī)制,一定程度上提高了軟件的安全性防止出現(xiàn)低級的失誤
2.不必等到運(yùn)行的時候才去強(qiáng)制轉(zhuǎn)換,可以直接獲取到相應(yīng)的數(shù)據(jù)類型,程序更加可讀。
public static void main(String[] args) {
Class c1 = new ArrayList<Integer>().getClass();
Class c2 = new ArrayList<String>().getClass();
System.out.println(c1 == c2);
System.out.println(c1);
}

但是不管是ArrayList<Integer>還是ArrayList<String>,在編譯時都會被編譯器擦除成了ArrayList.
那么如何獲取泛型參數(shù)的實(shí)際類型呢。由于通過getClass無法直接拿到該泛型的實(shí)際類型,但是在程序運(yùn)行時,類型是確定的,我們可以借助反射來達(dá)到我們的目的。
首先,聲明一個泛型類
public class Test<T, E, V> {
protected T dataT;
protected E dataE;
protected V dataV;
}
再聲明一個繼承該泛型的子類
public class Student extends Test<Student, Integer, String> {
private void test() {
Student dataT = this.dataT.dataT.dataT;
}
public static void main(String[] args) {
Student student = new Student();
Class clazz = student.getClass();
//獲得該類的父類
System.out.println(clazz.getSuperclass());
//獲得帶有泛型的父類
Type type = clazz.getGenericSuperclass();
System.out.println(type);
System.out.println("-------");
ParameterizedType parameterizedType = (ParameterizedType) type;
//參數(shù)化類型數(shù)組,泛型可能有多個
for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
System.out.println(typeArgument.getTypeName());
}
}
}

從上圖可以看出,我們可以借助反射通過getGenericSuperclass獲得父類泛型再強(qiáng)轉(zhuǎn)為ParameterizedType可以獲得參數(shù)化類型數(shù)組,并且得到所有的泛型類型
同時我們也可以看到getSuperclass和getGenericSuperclass的區(qū)別
getSuperclass返回直接繼承的父類,但是由于泛型擦除,沒有顯示泛型參數(shù)。
getGenericSuperclass返回直接繼承的父類,包含泛型參數(shù)
public class Test2 {
public static void main(String[] args) {
System.out.println("Student2.class.getSuperclass()--被泛型擦除的父類---" + Student2.class.getSuperclass());
System.out.println("Student2.class.getSuperclass()--沒有被泛型擦除的父類---" + Student2.class.getGenericSuperclass());
System.out.println("------");
System.out.println("Test2.class.getSuperclass()--被泛型擦除的父類---" + Test2.class.getSuperclass());
System.out.println("Test2.class.getSuperclass()--沒有被泛型擦除的父類---" + Test2.class.getGenericSuperclass());
System.out.println("------");
System.out.println("Object.class.getSuperclass()--被泛型擦除的父類---" + Object.class.getSuperclass());
System.out.println("Object.class.getSuperclass()--沒有被泛型擦除的父類---" + Object.class.getGenericSuperclass());
System.out.println("------");
System.out.println("int[].class.getSuperclass()--被泛型擦除的父類---" + int[].class.getSuperclass());
System.out.println("int[].class.getSuperclass()--沒有被泛型擦除的父類---" + int[].class.getGenericSuperclass());
System.out.println("------");
System.out.println("void.class.getSuperclass()--被泛型擦除的父類---" + void.class.getSuperclass());
System.out.println("void.class.getSuperclass()--沒有被泛型擦除的父類---" + void.class.getGenericSuperclass());
}
}
class Person2<T> {
}
class Student2 extends Person2<Test2> {
}

如果此 Class 表示 Object 類、接口、基本類型或 void,則返回 null。
如果此對象表示一個數(shù)組類,則返回表示 Object 類的 Class 對象。
再來看一段代碼
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
Map<Integer, String> map = new HashMap<Integer, String>();
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
}
這種聲明和getTypeParameters 方法仍然無法拿到具體的數(shù)據(jù)類型

通過上面的解釋,我們知道可以通過子類繼承父類的方法getGenericSuperclass再獲取type類型獲取泛型類型。
public class Test2 {
public static void main(String[] args) {
MyHashMap map = new MyHashMap();
System.out.println(map.getClass().getGenericSuperclass());
ParameterizedType genericSuperclass = (ParameterizedType) map.getClass().getGenericSuperclass();
for (Type a : genericSuperclass.getActualTypeArguments()) {
System.out.println(a);
}
}
}
class MyHashMap extends HashMap<Integer, String> {
}

但是每次都要聲明子類繼承父類的方式會不會太繁瑣呢,我們可以使用匿名內(nèi)部類簡化這一步驟
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>() {
};
System.out.println(map.getClass().getGenericSuperclass());
ParameterizedType genericSuperclass = (ParameterizedType) map.getClass().getGenericSuperclass();
for (Type a : genericSuperclass.getActualTypeArguments()) {
System.out.println(a);
}
}

所以無論是gson還是fastjson都是使用匿名內(nèi)部類巧妙的獲取被擦除的泛型信息。
gson是使用TypeToken
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String jsonData = "{\n" +
" \"name\": \"BeJson\"}";
Gson gson = new Gson();
DataBean bean = gson.fromJson(jsonData, DataBean.class);
Log.e("MainActivity", "bean name: " + bean.name);
Log.e("MainActivity", "bean jsonStr: " + gson.toJson(bean));
Foo<DataBean> foo = new Foo<DataBean>();
foo.value = bean;
Log.e("MainActivity", "foo jsonStr: " + gson.toJson(foo));
String genericData = "{\"value\":{\"name\":\"BeGeneric\"}}";
TypeToken<Foo<DataBean>> typeToken = new TypeToken<Foo<DataBean>>(){};
Foo<DataBean> genericBean = gson.fromJson(genericData, typeToken.getType());
Log.e("MainActivity", "generic bean value : " + gson.toJson(genericBean.value));
}
class DataBean{
public String name;
}
class Foo<T> {
T value;
}
}
fastjson使用的TypeReference
//通過創(chuàng)建TypeReference的匿名內(nèi)部類的方式來保留反省信息,以便json反序列化時能反射獲取到泛型實(shí)際類型
ResponseDTO<SysCryptDTO> responseDTO = JSONObject.parseObject(jsonString, new TypeReference<ResponseDTO<SysCryptDTO>>() {});