前边说过,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 [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