泛型系列文章(3) - 泛型高级知识 - 泛型与反射包的应用
Gson泛型解析原理
用过Gson的都知道,Gson是可以解析带复杂泛型参数的数据结构的,方式如下:
T data = new Gson().fromJson(json, new TypeToken<T>(){}.getType());
List<List<String>> data = new Gson().fromJson(json, new TypeToken<List<List<String>>>(){}.getType());
我们已经知道,Class在运行期间是会擦除具体泛型信息的,因为Java代码最终会被编译为机器指令,对CPU来说,并不需要知道对象的具体类型,只要知道参与运算的对象地址就行了。如果所有的泛型信息全部存储起来,是一个很大的开销。因此,想要告诉Gson库具体的泛型类型,传递Class对象肯定是不行的。通过上面的代码我们已经可以看出来,Gson是通过传递了一个特殊的Type对象来完成这个功能的。虽然Class会擦除具体泛型信息,但是Class是会记录实现了哪些接口,继承了哪些父类的type,可以通过getGenericSuperclass获取包含泛型信息的父类信息。我们以List为例来验证下这个知识点:
//ArrayList类声明
package java.util;
public class ArrayList<E> extends AbstractList<E> implements List<E> {
}
List<List<String>> list = new ArrayList();
//Class不会记录自身的泛型信息
System.out.println("Class Type: " + list.getClass().getTypeName());
//Class会记录类声明时父类的泛型信息
//由于ArrayList声明时并未指定父类的具体泛型,所以获取到的就是占位符<E>
System.out.println("Superc Class Type: " + list.getClass().getGenericSuperclass().getTypeName());
/*************输出结果**************/
Class Type: java.util.ArrayList
Superc Class Type: java.util.AbstractList<E>
可以看到,Class中确实保存了父类的泛型信息,不过这里的是通用占位符E,还不是具体的类型
Gson和我们的区别在于:我们是直接new了一个对象,而Gson在new后面还加上了一对大括号
大括号的作用是重写父类方法,所以new XXX() {}的本质其实于声明了一个匿名类继承XXX,然后用匿名类创建了一个对象,就相当于
class XXX extends TypeToken<List<List<String>>>
如上匿名类在声明时,其实就已经指定了具体的父类泛型参数,所以在运行时是可以获取到的,我们用List来验证下:
//new后面带上大括号,表示重写父类方法,创建了一个匿名子类
List<List<String>> list = new ArrayList<List<String>>(){};
//打印对象的实际类型
System.out.println("Class Type: " + list.getClass().getTypeName());
//打印对象父类信息
System.out.println("Super Class Type: " + list.getClass().getGenericSuperclass().getTypeName());
/*************输出结果**************/
Class Type: com.easing.java.Hello$1
Super Class Type: java.util.ArrayList<java.util.List<java.lang.String>>
可以看到,通过大括号重写创建的对象,确实是子类,而且可以正确获取到实际的泛型信息。
参考链接:
https://blog.csdn.net/u013718730/article/details/106165426/