引言

从这篇文章开始,我们来学习java中的反射机制,我们都能够在工作中使用Class、Method、Field这些类实现反射功能,但是可能对这些类到底都能做到什么,甚至这些类有怎样的结构层次都不是很清楚。我们先从Class类出发,介绍几个它的继承关系中比较重要的接口,了解它们不仅是因为它们在反射中经常出现,更重要的一点,这里面的很多接口都是java中的一些重要概念(类型变量、参数化类型)在语言层面的映射,理解了它们,我们就能更加熟悉java语言和语言特性。

Class的类结构层次

我们先看Class的定义:

  1. public final class Class<T> implements java.io.Serializable,
  2. GenericDeclaration,
  3. Type,
  4. AnnotatedElement {}

它实现了四个接口,如下图所示:
diagram.png
我们这篇文章重点来看Type接口和它的子接口。

Type及其子接口

Type接口出现的背景

这里先讲一下这个接口出现的背景:在泛型(jdk1.5)出现之前,java中只有以下几种类型:类、接口、简单类型和数组。其中类和接口都是原生类型(raw type),没有被泛化,这些类型用一个Class类就都能表示。
在泛型出现之后,就多出了参数化类型(泛型类、泛型接口)、类型变量类型、泛型数组类型,这些类型也需要有对应的类来表示。所以就出现了Type接口来作为Java编程语言中所有类型的公共接口。有了Type之后,Class类作为Type的子类,仍然表示类、接口、简单类型和数组,而参数化类型、类型变量类型和泛型数组类型,分别由ParameterizedType、TypeVariable和GenericArrayType这几个子接口来表示。另外还有一个WildcardType,它表示通配符表达式。这些子接口如下图所示:
implements.png
下面这个表格总结出了这些子接口与java语言中类型之间的映射关系:

java语言类型 对应的Type类
简单类型 Class
类(raw type) Class
接口(raw type) Class
数组 Class
参数化类型(泛型类、泛型接口) ParameterizedType
类型变量 TypeVariable
泛型数组 GenericArrayType

理解类型变量和参数化类型

在逐一介绍这几个接口之前,我们需要先理解类型变量和参数化类型是什么,我们结合《java语言规范》第八版中的定义来看:
类型变量是在类、接口、方法和构造器中用作类型的非限定标识符。
类型变量可以声明为泛化类声明、泛化接口声明、泛化方法声明和泛化构造器声明中的类型参数。
看这个例子:

  1. public class TypeVariableTest1<T> {
  2. }
  3. public class TypeVariableTest2<E extends SimpleObject> {
  4. }

其中,T和E这两个非限定标识符就是类型变量。
参数化类型是形式为C的类或接口,其中C是泛型名,而是表示该泛型的特定参数化形式的类型引元列表。
上面例子中的TypeVariableTest1和TypeVariableTest2就是参数化类型,其实就是泛型类和泛型接口。
结合实例,理解类型变量和参数化类型应该是比较容易的。

Type的定义

Type的声明如下:

  1. /**
  2. * Type is the common superinterface for all types in the Java
  3. * programming language. These include raw types, parameterized types,
  4. * array types, type variables and primitive types.
  5. *
  6. * @since 1.5
  7. */
  8. public interface Type {
  9. default String getTypeName() {
  10. return toString();
  11. }
  12. }

注释中的解释跟我们的理解基本一致。
Type接口只有一个方法getTypeName,默认实现是调用toString方法。

TypeVariable接口

接口声明与含义

TypeVariable接口就用来表示所有的类型变量。
它的声明如下:

  1. public interface TypeVariable<D extends GenericDeclaration>
  2. extends Type, AnnotatedElement {}

从声明中我们就能知道很多东西:首先,继承Type类,这个不用再阐述,类型变量也是java类型中的一种。其次,继承了AnnotatedElement接口,说明是可以被注解的,在java中,注解可以用在类型变量上面,最后,它自己也有类型变量,D extends GenericDeclaration,用来指定声明这个类型变量的元素的类型,例如是类、接口方法还是构造方法,我们知道,只有这四个能声明类型变量,而GenericDeclaration正好是这几个的父接口。

getBounds()方法

TypeVariable提供了四个方法,我们来逐个分析一下:

  1. Type[] getBounds();

getBounds方法用来获取类型变量的上边界,如果类型变量的上边界没有明确声明,就会返回Object,不管上边界是什么,肯定是一个类型(类、接口、类型变量、参数化类型),所以返回值是Type的数组。
看下面的例子:

  1. public class TypeVariableTest<T,E extends SimpleObject,F extends E> {
  2. public static void main(String[] args) {
  3. TypeVariable<Class<TypeVariableTest>>[] typeParameters = TypeVariableTest.class.getTypeParameters();
  4. for (TypeVariable<Class<TypeVariableTest>> typeParameter : typeParameters) {
  5. System.out.println(Arrays.toString(typeParameter.getBounds()));
  6. }
  7. }
  8. }

在讲GenericDeclaration这个接口时,我们知道该接口唯一提供的方法getTypeParameters方法就是用来获取这个GenericDeclaration(类、方法或者构造方法)声明的TypeVariable数组,所以这个示例中我们用这个方法来得到了TypeVariableTest这个类的类型变量数组。
输出:

  1. [class java.lang.Object]
  2. [class person.andy.concurrency.classload.SimpleObject]
  3. [E]

结果与注释中的说明一样,对于没有显式声明上边界的,返回的是Object,对于显式声明上边界的,直接返回上边界,类型变量F的上边界是E,这里直接输出E。

getGenericDeclaration()方法

  1. D getGenericDeclaration();

这个方法返回声明这个类型变量的GenericDeclaration,如果一个Class声明了这个类型变量,就返回这个Class,如果一个Method声明了这个类型变量,就返回这个Method,看下面的例子:

  1. public class TypeVariableTest<T,E extends SimpleObject,F extends E> {
  2. public <G extends SimpleObject> void test(G g){
  3. }
  4. public static void main(String[] args) throws NoSuchMethodException {
  5. TypeVariable<Class<TypeVariableTest>>[] typeParameters = TypeVariableTest.class.getTypeParameters();
  6. for (TypeVariable<Class<TypeVariableTest>> typeParameter : typeParameters) {
  7. System.out.println(typeParameter.getGenericDeclaration());
  8. }
  9. TypeVariableTest<String,SimpleObject,SimpleObject> typeVariableTest = new TypeVariableTest<>();
  10. Method test = typeVariableTest.getClass().getDeclaredMethod("test", SimpleObject.class);
  11. TypeVariable<Method>[] methodTypeParameters = test.getTypeParameters();
  12. for (TypeVariable<Method> methodTypeParameter : methodTypeParameters) {
  13. System.out.println(methodTypeParameter.getGenericDeclaration());
  14. }
  15. }
  16. }

输出:

  1. class person.andy.concurrency.reflect.TypeVariableTest
  2. class person.andy.concurrency.reflect.TypeVariableTest
  3. class person.andy.concurrency.reflect.TypeVariableTest
  4. public void person.andy.concurrency.reflect.TypeVariableTest.test(person.andy.concurrency.classload.SimpleObject)

我们对声明了类型变量的类和方法分别调用了getTypeParameters方法获取类型变量,然后调用getGenericDeclaration方法,返回了对应的Class和Method。

getName()方法

getName()方法返回类型变量的名称,与源代码中的名称一样。看下面的例子:

  1. public class TypeVariableTest<T,E extends SimpleObject,F extends E> {
  2. public static void main(String[] args) throws NoSuchMethodException {
  3. TypeVariable<Class<TypeVariableTest>>[] typeParameters = TypeVariableTest.class.getTypeParameters();
  4. for (TypeVariable<Class<TypeVariableTest>> typeParameter : typeParameters) {
  5. System.out.println(typeParameter.getName());
  6. }
  7. }
  8. }

返回的直接就是:

  1. T
  2. E
  3. F

ParameterizedType接口

接口声明与含义

ParameterizedType的定义如下:

  1. public interface ParameterizedType extends Type {}

ParameterizedType用来表示所有的参数化类型(泛型类、泛型接口)。

getActualTypeArguments()方法

  1. Type[] getActualTypeArguments();

这个方法返回一个Type数组,数组里面的每个Type表示一个这个泛型类、泛型接口的类型参数。
类型参数可以这样解释:如果类声明了一个或者多个类型变量,那么它就是泛化的,这些类型变量被称为该类的类型参数。也就是说,类型参数就是泛型类或泛型接口中的类型变量。
看下面的例子:

  1. public class ParameterizedTypeTest<T,E> {
  2. public static void main(String[] args) {
  3. Class<ParameterizedTypeTestSub> parameterizedTypeTestClass = ParameterizedTypeTestSub.class;
  4. Type genericSuperclass = parameterizedTypeTestClass.getGenericSuperclass();
  5. ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
  6. Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
  7. for (Type actualTypeArgument : actualTypeArguments) {
  8. System.out.println(actualTypeArgument);
  9. }
  10. }
  11. }
  12. class ParameterizedTypeTestSub extends ParameterizedTypeTest<String, SimpleObject> {
  13. }

Class类的getGenericSuperclass()方法会返回Class代表的类的参数化的父类,这里就是ParameterizedTypeTest,然后调用getActualTypeArguments()方法拿到ParameterizedTypeTest的类型参数,输出如下:

  1. class java.lang.String
  2. class person.andy.concurrency.classload.SimpleObject

输出的是准确的类型参数的类型。

getRawType()方法

这个方法返回这个参数化类型(泛型类、泛型接口)的原生类型(raw type)。这个应该很好理解,看下面的例子:

  1. public class ParameterizedTypeTest<T,E> {
  2. public static void main(String[] args) {
  3. Class<ParameterizedTypeTestSub> parameterizedTypeTestClass = ParameterizedTypeTestSub.class;
  4. Type genericSuperclass = parameterizedTypeTestClass.getGenericSuperclass();
  5. ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
  6. System.out.println(parameterizedType.getRawType());
  7. }
  8. }
  9. class ParameterizedTypeTestSub extends ParameterizedTypeTest<String, SimpleObject> {
  10. }

输出:

  1. class person.andy.concurrency.reflect.type.ParameterizedTypeTest

输出的就是ParameterizedTypeTest这个原生类型。

getOwnerType()方法

这个方法返回这个参数化类型作为member所属的类型。例如,如果当前参数化类型是o.I,会返回0。看下面的例子:

  1. public class ParameterizedTypeTest<T,E> {
  2. public Map.Entry<String,Object> entry;
  3. public static void main(String[] args) throws NoSuchFieldException {
  4. Class<ParameterizedTypeTest> parameterizedTypeTestClass1 = ParameterizedTypeTest.class;
  5. Field entry = parameterizedTypeTestClass1.getDeclaredField("entry");
  6. ParameterizedType genericType = (ParameterizedType) entry.getGenericType();
  7. System.out.println(genericType.getOwnerType());
  8. }
  9. }

输出:

  1. interface java.util.Map

因为Entry是在Map中定义的,所以返回它的所有者就是Map。

小结

泛型的出现使得一个Class不能表示所有的类,类型变量、参数化类型、泛型数组,这些新的类型(type)同样需要在反射中用到,比如,我们可能想获取某个泛型类的类型参数。这样就出现了Type接口,这个接口作为java编程语言中所有类型的公共接口,不同的子类分别代表不同的类型。
除了Class,最重要的就是TypeVariable(类型变量)和ParameterizedType(参数化类型)这两个,它们分别提供了一些方法来获取对应类型特性相关的信息。
之后,我们会在反射中继续看到这些接口的用处。