【Java】java.lang.reflect.Type详解

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<String> list = new LinkedList();
	Class<? extends List> 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<E>
	Generic Interface: java.util.Deque<E>
	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<E>

数组的Class与Type


	String[][][][] array = new String[][][][]{};
	Class<? extends String[][][][]> 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<String> list;
	    public Map<Integer, Long> 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<java.lang.String>
	Generic Type: java.util.Map<java.lang.Integer, java.lang.Long>
	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<String> list;
	    public Map<Integer, Long> map;
	    public Map.Entry<Integer, Long> 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<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,还不是具体的类型
聪明的朋友应该已经猜到,区别在于我们是直接new了一个对象,而Gson在new后面还加上了一对大括号
大括号的作用是重写父类方法,所以new XXX() {}的本质其实于声明了一个匿名类继承XXX,然后用匿名类创建了一个对象

讲到这里,基础扎实的朋友应该已经恍然大悟,完全可以自己推测后面的事情了
这里再多说一局题外话,技术上想要走的远,光靠上班工作是不够的,业余时间还要多研究底层原理,多设计东西
有的人看到这里已经不用多说了,而平时不怎么学习的,一下接收这么多信息可能还没反应过来,这就是平时积累上的差距

我们看到,Gson在new TypeToken时是带上了泛型的,这其实就相当于


	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>>

可以看到,通过大括号重写创建的对象,确实是子类,而且可以正确获取到实际的泛型信息

模拟Gson的TypeToken类存储泛型信息

这里我们手写一个TypeMarker类,来模拟TypeToken的功能


	import java.lang.reflect.ParameterizedType;
	import java.lang.reflect.Type;
	
	//定义为抽象类,从而强制开发者重写继承此类
	public abstract class TypeMarker<T> {
	
	    //获取泛型参数类型
	    protected Type genericParamType() {
	        //获取父类类型
	        //由于是抽象类,其实现类必然是继承当前类,所以父类类型即是TypeMarker<XXX>
	        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<List<List<List<String >>>>  typeMarker = new TypeMarker<List<List<List<String >>>>(){};
	System.out.println("Param Type: " + typeMarker.genericParamType());


	Param Type: java.util.List<java.util.List<java.util.List<java.lang.String>>>

OK!格式是不是和Gson的一模一样,功能也完全达到

这里我们只是为了证明,泛型信息是可以保存并传递给Gson库的
实际Gson库中的TypeToken类比这个复杂,因为它还要完成其它功能
类内部的字段可能还包含泛型参数,要完成任意泛型类的完整解析,必须进行递归解析,所以必须包含其它功能

TypeToken写法优化

如果觉得TypeToken的写法很长很难看的话,自己写一个类继承泛型参数,再获取这个类的父类Type就行了


	//通用的HttpResponse类
	//用于接收Http请求返回的Body数据
	public class ResponseBody<T> {
	
	    public Integer code;
	    public String message;
	    public String[] detail;
	    public T data;
	}
	

	//接收登录请求返回的数据
	public class LoginResponse extends ResponseBody<User> {
	
	    public static Type type() {
	        return LoginResponse.class.getGenericSuperclass();
	    }
	}


	//Gson写法
	Type type1 = new TypeToken<ResponseBody<User>>(){}.getType();
	//优化后的写法
	Type type2 = LoginResponse.type();
	//获取到的Type类型一致
	System.out.println(type1.getTypeName());
	System.out.println(type2.getTypeName());
	//Gson解析
	//注意,这里的Type是ResponseBody<User>,所以也要用这个类型来接收
	ResponseBody<User> response = new Gson().fromJson(json, type2);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章