一、使用泛型的好处
1.泛型确定了具体的数据类型,对于List,Map这样的数据容器提供了类型检测机制,只有相匹配的数据才能正常的赋值,否则编译器就不通过,它是一种类型安全检测机制,一定程度上提高了软件的安全性防止出现低级的失误
2.不必等到运行的时候才去强制转换,可以直接获取到相应的数据类型,程序更加可读。
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);
}
image.png
但是不管是ArrayList<Integer>还是ArrayList<String>,在编译时都会被编译器擦除成了ArrayList.
那么如何获取泛型参数的实际类型呢。由于通过getClass无法直接拿到该泛型的实际类型,但是在程序运行时,类型是确定的,我们可以借助反射来达到我们的目的。
首先,声明一个泛型类
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;
//参数化类型数组,泛型可能有多个
for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
System.out.println(typeArgument.getTypeName());
}
}
}
从上图可以看出,我们可以借助反射通过getGenericSuperclass获得父类泛型再强转为ParameterizedType可以获得参数化类型数组,并且得到所有的泛型类型
同时我们也可以看到getSuperclass和getGenericSuperclass的区别
getSuperclass返回直接继承的父类,但是由于泛型擦除,没有显示泛型参数。
getGenericSuperclass返回直接继承的父类,包含泛型参数
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> {
}
image.png
如果此 Class 表示 Object 类、接口、基本类型或 void,则返回 null。
如果此对象表示一个数组类,则返回表示 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 方法仍然无法拿到具体的数据类型
image.png
通过上面的解释,我们知道可以通过子类继承父类的方法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> {
}
image.png
但是每次都要声明子类继承父类的方式会不会太繁琐呢,我们可以使用匿名内部类简化这一步骤
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);
}
}
image.png
所以无论是gson还是fastjson都是使用匿名内部类巧妙的获取被擦除的泛型信息。
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
//通过创建TypeReference的匿名内部类的方式来保留反省信息,以便json反序列化时能反射获取到泛型实际类型
ResponseDTO<SysCryptDTO> responseDTO = JSONObject.parseObject(jsonString, new TypeReference<ResponseDTO<SysCryptDTO>>() {});
网友评论