前边说过,JDK1.5推出了反射功能,并介绍了反射的类设计结构,其中提到了 Type 接口,它是JDK1.5新增的接口,表示Java中的所有类型。本文介绍这个接口及其五大组件。

1. Type的五大组件概览
Type 接口表示Java中的类型,这些类型包括原始类型、参数化类型、数组类型、类型变量和基本类型,前边介绍的 Class 就是其组件之一。Type 及其组件用于通过反射来描述Java各种类型,比如描述泛型的定义,获取泛型参数个数、类型、泛型通配符的上下边界等等。一个典型的应用场景是获取真实的泛型类型,我们将在后续文章单独介绍。
Type 用其五大组件来描述上边提到这些类型,类图如下:

ParameterizedType: 表示参数化类型,即整个泛型的定义,如
List<String>可以用ParameterizedType来描述;TypeVariable: 表示类型变量,即泛型申明中的具体类型,比如:
List<E>用参数化类型来描述,而其中的E就用TypeVariable来描述;WildcardType: 表示泛型通配符类型,即泛型申明中带
?通配符,比如:List<? extends Number>中? extends Number就用WildcardType来描述;GenericArrayType: 表示泛型数组,比如申明一个泛型数组的域
T[] array,它就用GenericArrayType来描述;Class: 表示运行时的类或接口,在前边Java反射的体系结构一文中已经介绍过了,不再赘述。
一句话总结,Type 的 Class 组件用来描述Java类或接口等原始类型和基本类型,其他几个组件描述泛型类型,包括泛型的变量、通配符以及泛型数组。
现在,来看看这几个组件的用法。
2. 参数化类型ParameterizedType
什么是参数化类型?其实就是指泛型,具体而言,JDK5推出泛型之后,原来的具体的类型可以申明为泛型,变得"可被参数化"了,比如:List 是一个具体类型,但是可以申明为 List<String>、List<Integer> ,就仿佛可以申明不同的参数,类似方法可以申明不同的参数一样(其实泛型是编译期特性,它两实际上是同一类型,都是 List,这在 Java反射之创建泛型数组 已经说过了)。
所以,ParameterizedType 用来描述泛型类型,比如:List<T>、List<String>、User<T extends Number>、List<? super User> 都可以用它来描述。ParameterizedType 的定义如下:
public interface ParameterizedType extends Type {
Type[] getActualTypeArguments(); (1)
Type getRawType(); (2)
Type getOwnerType(); (3)
}| 1 | 获取参数化类型中的实际类型,就是其泛型类型,返回 Type 子组件的数组 |
| 2 | 返还原始类型,即:申明参数化类型的类或接口 |
| 3 | 返回申明该参数化类型的父类型,即:申明内部类、接口等成员的外层类或接口,主要用于内部类、接口是参数化类型时 |
来看一个例子,申明一个测试类,代码如下:
public class ParameterizedTypeDemo {
private List<String> list; (1)
class TopParameterizedType<T> { (2)
}
class InnerParameterizedType extends TopParameterizedType<String> { (3)
}
private <T> void genericMethod(List<T> list) { (4)
}
}| 1 | 申明泛型成员域 |
| 2 | 申明泛型内部类 |
| 3 | 申明泛型内部内子类 |
| 4 | 申明泛型方法 |
现在,编写一个测试方法:
public void testGenericClass() {
Type genericSuperclass = InnerParameterizedType.class.getGenericSuperclass(); (1)
assert genericSuperclass instanceof ParameterizedType; (2)
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
System.out.println("泛型类型:" + Arrays.toString(parameterizedType.getActualTypeArguments()));
System.out.println("原始类型:" + parameterizedType.getRawType());
System.out.println("所有者类型:" + parameterizedType.getOwnerType()); (3)
}| 1 | getGenericSuperclass 方法可以拿到泛型父类,从而获取父类上定义的泛型类型 |
| 2 | 泛型父类类型是 ParameterizedType |
| 3 | 由于 InnerParameterizedType 是 ParameterizedTypeDemo 的内部类,所以所有者是 ParameterizedTypeDemo |
这里仅展示了测试泛型类的示例,关于泛型成员域、泛型方法的示例代码见文末的源码。 |
示例中,TopParameterizedType 类被申明为泛型类,用 Class 的 getGenericSuperclass 方法获取了子类的父泛型类,由于类申明的泛型类型不能直接创建实例(比如,不能直接 new TopParameterizedType<T>()),因此只能通过继承父类来明确子类的具体泛型类型,然后才能通过 ParameterizedType 的 getActualTypeArguments() 获取到子类具体的泛型类型(这里为 String),这就是如何获取类上的具体泛型类型的方法,后续文章会继续讨论。
示例输出结果如下:
泛型类型:[class java.lang.String] 原始类型:class com.belonk.lang.reflect.ParameterizedTypeDemo$TopParameterizedType 所有者类型:class com.belonk.lang.reflect.ParameterizedTypeDemo
|
参数化类型描述了整个泛型,我们知道,泛型可以申明泛型参数,可以申明通配符或者定义上下边界,它们就用 TypeVariable 和 WildcardType 来描述。
要理解
所以,具有通配符的泛型类型变量用 |
3. 类型变量TypeVariable
TypeVariable 用来描述类型变量,即泛型定义中的参数。其定义如下:
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
Type[] getBounds(); (1)
D getGenericDeclaration(); (2)
String getName(); (3)
AnnotatedType[] getAnnotatedBounds(); (4)
}| 1 | 获取类型变量的上边界,注意类型变量没有下边界,这里 已经说过了 |
| 2 | 获取类型变量的泛型申明类和接口,即泛型的申明者 |
| 3 | 获取类型变量的名称 |
| 4 | JDK1.8新增的方法,返回一个 AnnotatedType 对象数组,如果类型变量上边界中申明了注解则可以用来获取注解。可以看做是 getBounds() 方法的扩展,除了获取到上边界的具体类型,还可以获取其标注的注解。AnnotatedType 数组中对象的顺序对应于类型参数声明中边界的顺序。如果类型参数声明没有边界,则返回长度为 0 的数组。 |
从 TypeVariable 的定义可以看到,它持有一个泛型对象 D,上边界为 GenericDeclaration 接口,在 Java反射的体系结构 中说过,GenericDeclaration 是用来获取类型变量的接口,它继承自 AnnotatedElement 接口:
public interface GenericDeclaration extends AnnotatedElement {
public TypeVariable<?>[] getTypeParameters(); (1)
}| 1 | 获取类型变量,返回 TypeVariable 数组 |
而 Class 实现了该接口,因此,我们可以通过 Class 的 getTypeParameters() 来获取类上的类型变量信息。
关于 |
现在,来看一个示例:
public class TypeVariableDemo {
class TypeVariableTestClass<T extends Number & Serializable> { (1)
// ...省略部分代码
}
public void testGenericClass() {
TypeVariable<Class<TypeVariableTestClass>>[] typeVariables = TypeVariableTestClass.class.getTypeParameters(); (2)
for (TypeVariable<Class<TypeVariableTestClass>> typeVariable : typeVariables) {
System.out.println(typeVariable);
System.out.println(Arrays.toString(typeVariable.getBounds()));
System.out.println(typeVariable.getGenericDeclaration());
System.out.println(typeVariable.getName());
System.out.println(Arrays.toString(typeVariable.getAnnotatedBounds()));
}
}
}| 1 | 申明泛型类,定义了上边界为 Number 和 Serializable 接口 |
| 2 | 通过 Class 的 getTypeParameters() 方法获取类上的泛型变量 |
上边的示例可以看到,getBounds() 方法返回了具体的上边界类,泛型申明类为 TypeVariableDemo$TypeVariableTestClass, 泛型参数名称为 T,而 getAnnotatedBounds() 返回的注解申明类型上边界是 AnnotatedType 的具体实现 AnnotatedTypeFactory$AnnotatedTypeBaseImpl 对象。
testGenericClass() 方法输出结果如下:
T [class java.lang.Number, interface java.io.Serializable] class com.belonk.lang.reflect.TypeVariableDemo$TypeVariableTestClass T [sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@548c4f57, sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@1218025c]
|
4. 通配符类型WildcardType
如果泛型参数申明为通配符的形式,那么就用 WildcardType 来描述之。前边说过, 通配符可以定义泛型的上下边界,因此,WildcardType 一定是可以获取上下边界的具体类型。其定义如下:
public interface WildcardType extends Type {
Type[] getUpperBounds(); (1)
Type[] getLowerBounds(); (2)
}| 1 | 获取泛型通配符定义的上边界类型 |
| 2 | 获取泛型通配符定义的下边界类型 |
可以看到,WildcardType 定义了两个分别获取上、下边界的方法,返回一个 Type 数组。
那么,如果能够获取到 WildcardType 呢?前边说过,泛型通配符只能用在成员域、方法参数(包括构造函数参数)中,因此,获取这些泛型通配符类型肯定在它们对应的反射类中。在 Field 类中,定义了一个 getGenericType() 方法,用来获取成员域定义的泛型类型;而对于 Constructor 和 Method,在 Java反射的体系结构 中说过,它们都继承自 Executable ,该对象定义了一个 getGenericParameterTypes() 方法来获取参数的泛型类型。
看一个示例:
public class WildcardTypeDemo {
class WildcardTypeClass {
private List<? extends Number> numberList; (1)
private List<? super Integer> intList; (1)
public WildcardTypeClass(List<? extends Number> numberList) { (2)
}
public WildcardTypeClass(Set<? super Number> intSet) { (2)
}
public void method(List<? extends Number> numberList) { (3)
}
public void method(Set<? super Integer> intList) { (3)
}
}
}| 1 | 定义泛型通配符带上下边界的成员域 |
| 2 | 定义构造函数,参数申明了泛型通配符 |
| 3 | 定义方法,参数申明了泛型通配符 |
为了简单起见,这里仅展示了成员域的泛型通配符上下边界类型示例,构造器和方法的示例代码可以看文末的源码。示例代码如下:
public void testGenericField() {
try {
Field numberList = WildcardTypeClass.class.getDeclaredField("numberList");
Type genericType = numberList.getGenericType(); (1)
assert genericType instanceof ParameterizedType;
ParameterizedType parameterizedType = (ParameterizedType) genericType; (2)
Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0]; (3)
assert actualTypeArgument instanceof WildcardType;
WildcardType wildcardType = (WildcardType) actualTypeArgument;
System.out.println(Arrays.toString(wildcardType.getUpperBounds())); (4)
System.out.println(Arrays.toString(wildcardType.getLowerBounds())); (4)
parameterizedType = (ParameterizedType) WildcardTypeClass.class.getDeclaredField("intList").getGenericType();
wildcardType = (WildcardType) parameterizedType.getActualTypeArguments()[0];
System.out.println(Arrays.toString(wildcardType.getUpperBounds()));
System.out.println(Arrays.toString(wildcardType.getLowerBounds()));
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}| 1 | 获取成员域的泛型类型 |
| 2 | 获取到的类型首先是一个参数化类型,它代表了整个泛型定义,比如 List<? extends Number> |
| 3 | 通过参数化类型再获取实际的泛型类型,由于定义了通配符,所以这里的实际类型就是 WildcardType |
| 4 | 打印通配符类型定义的上下边界 |
testGenericField() 方法输出如下的结果:
[class java.lang.Number] [] [class java.lang.Object] [class java.lang.Integer]
可以看到,numberList 域的上边界为 Number,而下边界未定义;intList 域下边界为 Integer,上边界为 Object。
5. 泛型数组类型GenericArrayType
Type 的 GenericArrayType 组件,用以描述泛型数组类型。在Java反射之创建泛型数组一文中,介绍了如何创建泛型数组,那么如何知道泛型数组的真实类型呢?这就是 GenericArrayType 设计的目的,其定义如下:
public interface GenericArrayType extends Type {
Type getGenericComponentType(); (1)
}| 1 | 获取泛型数组真实的类型 |
它只定义了一个方法,用来获取泛型数组真实的类型。看一个示例:
public class GenericArrayTypeDemo {
class GenericArrayTypeClass<T> { (1)
private String[] array0; (2)
private T[] array; (3)
private T[][] array1; (4)
private List<T>[] listArray; (5)
private List<? extends Number>[] numberArray; (6)
}
}| 1 | 定义泛型类 |
| 2 | 定义普通数组 |
| 3 | 定义泛型数组 |
| 4 | 定义泛型二维数组 |
| 5 | 定义泛型数组,类型是 List<T>,它仍然是一个泛型 |
| 6 | 定义上边界通配符泛型的泛型数组,类型为 List<? extends Number> |
分别测试这几种泛型数组申明:
1、下边的代码,展示了普通非泛型数组 array0 的类型获取:
Type array0 = GenericArrayTypeClass.class.getDeclaredField("array0").getGenericType();
System.out.println(array0);
System.out.println(array0.getClass());非泛型数组,通过 Field 的 getGenericType() 方法获取到的 Type 其实是一个 Class,这个 Class 很明显是 String 数组:
class [Ljava.lang.String; class java.lang.Class
2、对应泛型数组 array,其类型如下:
Type genericType = GenericArrayTypeClass.class.getDeclaredField("array").getGenericType();
assert genericType instanceof GenericArrayType;
GenericArrayType genericArrayType = (GenericArrayType) genericType;
System.out.println(genericArrayType.getGenericComponentType());
System.out.println(genericArrayType.getGenericComponentType().getClass());输出:
T class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
可以看到,获取到的实际类型是类型变量 TypeVariable。
3、对应二维数组 array1:
genericArrayType = (GenericArrayType) GenericArrayTypeClass.class.getDeclaredField("array1").getGenericType();
System.out.println(genericArrayType.getGenericComponentType());
System.out.println(genericArrayType.getGenericComponentType().getClass());输出如下:
T[] class sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
可以看到,其 Type 仍然是一个 GenericArrayType, 可以继续向下获取最终泛型数组的真实类型。
4、对于 listArray,它是一个参数化类型,泛型参数为类上定义的泛型类型 T:
genericArrayType = (GenericArrayType) GenericArrayTypeClass.class.getDeclaredField("listArray").getGenericType();
System.out.println(genericArrayType.getGenericComponentType());
System.out.println(genericArrayType.getGenericComponentType().getClass());结果输出:
java.util.List<T> class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
结果同预想的一致,确实是参数化类型。
5、对于 numberArray,同样也是参数化类型,只是它定义了通配符,因此可以继续获取到 WildcardType:
genericArrayType = (GenericArrayType) GenericArrayTypeClass.class.getDeclaredField("numberArray").getGenericType();
System.out.println(genericArrayType.getGenericComponentType());
System.out.println(genericArrayType.getGenericComponentType().getClass());
Type genericComponentType = genericArrayType.getGenericComponentType();
assert genericComponentType instanceof ParameterizedType;
Type actualTypeArguments = ((ParameterizedType) genericComponentType).getActualTypeArguments()[0]; (1)
System.out.println(actualTypeArguments);
System.out.println(actualTypeArguments.getClass());| 1 | 通过参数化类型,继续向下获取实际类型参数,这里是 WildcardType |
结果输出如下:
java.util.List<? extends java.lang.Number> class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl ? extends java.lang.Number class sun.reflect.generics.reflectiveObjects.WildcardTypeImpl
可以看到,结果同我们预想的一致,通过 genericComponentType 获取到的确实是 WildcardType。
6. 总结
Type 接口是JDK1.5新增的表示Java所有类型的接口,它有5个组件,除了Java的类、接口的原始类型可以用 Class 来描述,其他的几个组件都是用来描述泛型类型的,分别是描述整个泛型的 ParameterizedType、描述泛型类型变量的 TypeVariable、描述泛型通配符的 WildcardType,以及描述泛型数组的 GenericArrayType。
通常,根据泛型定义的形式,会先获取 ParameterizedType,然后通过其 getActualTypeArguments() 继续深挖到其他泛型类型。
本文示例代码见: github.