Type简介
Type是Java反射中框架中的一个核心接口,用于获取类型信息,它的定义很简单
package java.lang.reflect;
public interface Type {
default String getTypeName() {
return this.toString();
}
}
我们在日常开发中,几乎用不到这个接口,但在底层的JDK源码中,Type则是一个非常重要的接口 我们常见的Class类就实现了Type接口,Type接口的另一个重要实现类是ParameterizedType Class类只保存了当前类的基本类型信息,而ParameterizedType则保存了泛型,外部类等额外类型信息
如果我们想学得更加深入,自己写出像Gson那样的类型解析库,支持泛型嵌套等各种复杂数据类型,则必须学习这些知识
下面我们来逐个学习下Type在Java源码中的一些基本用法
获取Class的接口实现和类继承信息
List list = new LinkedList();
Class clazz = list.getClass();
//获取接口类型信息
Type[] genericInterfaces = clazz.getGenericInterfaces();
for (Type genericInterface : genericInterfaces)
System.out.println("Generic Interface:" + genericInterface.getTypeName());
//打印Type的具体类型
for (Type genericInterface : genericInterfaces)
System.out.println("Type Class:" + genericInterface.getClass().getName());
//获取父类类型信息
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println("Generic Superclass:" + genericSuperclass.getTypeName());
//可见,通过Type可以保存Class的所有接口实现和类继承信息
//由于Class中的泛型信息会被参数,TypeName只保留了类声明时的泛型信息
//另外可以发现,带泛型的Type都是通过ParameterizedType类型保存的,无泛型的Type则是通过Class来保存的
Generic Interface: java.util.List
Generic Interface: java.util.Deque
Generic Interface: java.lang.Cloneable
Generic Interface: java.io.Serializable
Type Class: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
Type Class: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
Type Class: java.lang.Class
Type Class: java.lang.Class
Generic Superclass: java.util.AbstractSequentialList
数组的Class与Type
String[][][][] array = new String[][][][]{};
Class clazz = array.getClass();
//获取数组的Class和Type
System.out.println("Class Name: " + clazz.getName());
System.out.println("Type Name: " + clazz.getTypeName());
//获取数组基类类型
Class componentType = clazz.getComponentType();
System.out.println("Component Type: " + componentType.getTypeName());
Class Name: [[[[Ljava.lang.String;
Type Name: java.lang.String[][][][]
Component Type: java.lang.String[][][]
获取Field的字段类型
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class Hello {
public String name;
public List list;
public Map map;
public Hello.Inner inner;
public static class Inner {
}
public static void main(String[] args) throws Exception {
Field[] fields = Hello.class.getDeclaredFields();
//获取字段的基本类型
for (Field field : fields) {
Class type = field.getType();
System.out.println("Basic Type: " + type.getName());
}
//获取字段的泛型类型
for (Field field : fields) {
Type genericType = field.getGenericType();
System.out.println("Generic Type: " + genericType.getTypeName());
}
//获取Type的实际类型
for (Field field : fields) {
Type genericType = field.getGenericType();
System.out.println("Type Class: " + genericType.getClass().getName());
}
}
}
Basic Type: java.lang.String
Basic Type: java.util.List
Basic Type: java.util.Map
Basic Type: com.easing.java.Hello$Inner
Generic Type: java.lang.String
Generic Type: java.util.List
Generic Type: java.util.Map
Generic Type: com.easing.java.Hello$Inner
Type Class: java.lang.Class
Type Class: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
Type Class: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
Type Class: java.lang.Class
ParameterizedType
上面提到了,相比Class只保存基本类型信息,ParameterizedType还会保存泛型参数等额外的类型信息
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class Hello {
public String name;
public List list;
public Map map;
public Map.Entry entry;
public static void main(String[] args) throws Exception {
Field field = Hello.class.getDeclaredField("entry");
ParameterizedType type = (ParameterizedType) field.getGenericType();
//获取字段的基本类型
Type rawType = type.getRawType();
System.out.println("Raw Type: " + rawType.getTypeName());
//获取字段泛型参数的类型
Type[] argumentTypes = type.getActualTypeArguments();
for (Type argumentType : argumentTypes)
System.out.println("Argument Type: " + argumentType.getTypeName());
//如果字段类型是个内部类,则获取字段所在外部类的类型
Type ownerType = type.getOwnerType();
System.out.println("Owner Type: " + ownerType.getTypeName());
}
}
Raw Type: java.util.Map$Entry
Argument Type: java.lang.Integer
Argument Type: java.lang.Long
Owner Type: java.util.Map
Gson泛型解析原理
用过Gson的都知道,Gson是可以解析带复杂泛型参数的数据结构的,方式如下
T data = new Gson().fromJson(json, new TypeToken(){}.getType());
List data = new Gson().fromJson(json, new TypeToken(){}.getType());
我们已经知道,Class在运行期间是会擦除具体泛型信息的,因为Java代码最终会被编译为机器指令,对CPU来说,并不需要知道对象的具体类型,只要知道参与运算的对象地址就行了。如果所有的泛型信息全部存储起来,是一个很大的开销
因此,想要告诉Gson库具体的泛型类型,传递Class对象肯定是不行的。通过上面的代码我们已经可以看出来,Gson是通过传递了一个特殊的Type对象来完成这个功能的
虽然Class会擦除具体泛型信息,但是上面已经提到过,Class是会通过Type记录自己实现了哪些接口,继承了哪些父类的,可以通过getGenericSuperclass获取
我们以List为例来验证下这个知识点
//ArrayList类声明
package java.util;
public class ArrayList extends AbstractList implements List {
}
List list = new ArrayList();
//Class不会记录自身的泛型信息
System.out.println("Class Type: " + list.getClass().getTypeName());
//Class会记录类声明时父类的泛型信息
//由于ArrayList声明时并未指定父类的具体泛型,所以获取到的就是占位符
System.out.println("Superc Class Type: " + list.getClass().getGenericSuperclass().getTypeName());
Class Type: java.util.ArrayList
Superc Class Type: java.util.AbstractList
可以看到,Class中确实保存了父类的泛型信息,不过这里的是通用占位符E,还不是具体的类型 聪明的朋友应该已经猜到,区别在于我们是直接new了一个对象,而Gson在new后面还加上了一对大括号 大括号的作用是重写父类方法,所以new XXX() {}的本质其实于声明了一个匿名类继承XXX,然后用匿名类创建了一个对象
讲到这里,基础扎实的朋友应该已经恍然大悟,完全可以自己推测后面的事情了 这里再多说一局题外话,技术上想要走的远,光靠上班工作是不够的,业余时间还要多研究底层原理,多设计东西 有的人看到这里已经不用多说了,而平时不怎么学习的,一下接收这么多信息可能还没反应过来,这就是平时积累上的差距
我们看到,Gson在new TypeToken时是带上了泛型的,这其实就相当于
class XXX extends TypeToken
匿名类在声明时,其实就已经指定了具体的父类泛型参数,所以在运行时是可以获取到的,我们仍然用List来验证下
//new后面带上大括号,表示重写父类方法,创建了一个匿名子类
List list = new ArrayList(){};
//打印对象的实际类型
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
可以看到,通过大括号重写创建的对象,确实是子类,而且可以正确获取到实际的泛型信息
模拟Gson的TypeToken类存储泛型信息
这里我们手写一个TypeMarker类,来模拟TypeToken的功能
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
//定义为抽象类,从而强制开发者重写继承此类
public abstract class TypeMarker {
//获取泛型参数类型
protected Type genericParamType() {
//获取父类类型
//由于是抽象类,其实现类必然是继承当前类,所以父类类型即是TypeMarker
Type superType = getClass().getGenericSuperclass();
//如果没有指定泛型参数,则返回的Type实际类型为Class
//未指定泛型参数时,默认将泛型视为Object类
if (superType instanceof Class)
return Object.class;
//如果有泛型参数,则返回的Type实际类型为ParameterizedType
//强转并获取泛型参数,即XXX的实际类型
ParameterizedType parameterizedType = (ParameterizedType) superType;
Type argumentType = parameterizedType.getActualTypeArguments()[0];
return argumentType;
}
}
TypeMarker typeMarker = new TypeMarker(){};
System.out.println("Param Type: " + typeMarker.genericParamType());
Param Type: java.util.List
OK!格式是不是和Gson的一模一样,功能也完全达到
这里我们只是为了证明,泛型信息是可以保存并传递给Gson库的 实际Gson库中的TypeToken类比这个复杂,因为它还要完成其它功能 类内部的字段可能还包含泛型参数,要完成任意泛型类的完整解析,必须进行递归解析,所以必须包含其它功能
TypeToken写法优化
如果觉得TypeToken的写法很长很难看的话,自己写一个类继承泛型参数,再获取这个类的父类Type就行了
//通用的HttpResponse类
//用于接收Http请求返回的Body数据
public class ResponseBody {
public Integer code;
public String message;
public String[] detail;
public T data;
}
//接收登录请求返回的数据
public class LoginResponse extends ResponseBody {
public static Type type() {
return LoginResponse.class.getGenericSuperclass();
}
}
//Gson写法
Type type1 = new TypeToken(){}.getType();
//优化后的写法
Type type2 = LoginResponse.type();
//获取到的Type类型一致
System.out.println(type1.getTypeName());
System.out.println(type2.getTypeName());
//Gson解析
//注意,这里的Type是ResponseBody,所以也要用这个类型来接收
ResponseBody response1 = new Gson().fromJson(json, type2);
//当然,我们也可以直接使用Class来解析
//Gson既然能够知道LoginResponse中的实际泛型,就完全可以帮我们完成自动解析
LoginResponse response2 = new Gson().fromJson(json, LoginResponse.class);