引言
从这篇文章开始,我们来学习java中的反射机制,我们都能够在工作中使用Class、Method、Field这些类实现反射功能,但是可能对这些类到底都能做到什么,甚至这些类有怎样的结构层次都不是很清楚。我们先从Class类出发,介绍几个它的继承关系中比较重要的接口,了解它们不仅是因为它们在反射中经常出现,更重要的一点,这里面的很多接口都是java中的一些重要概念(类型变量、参数化类型)在语言层面的映射,理解了它们,我们就能更加熟悉java语言和语言特性。
Class的类结构层次
我们先看Class的定义:
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {}
它实现了四个接口,如下图所示:
我们这篇文章重点来看Type接口和它的子接口。
Type及其子接口
Type接口出现的背景
这里先讲一下这个接口出现的背景:在泛型(jdk1.5)出现之前,java中只有以下几种类型:类、接口、简单类型和数组。其中类和接口都是原生类型(raw type),没有被泛化,这些类型用一个Class类就都能表示。
在泛型出现之后,就多出了参数化类型(泛型类、泛型接口)、类型变量类型、泛型数组类型,这些类型也需要有对应的类来表示。所以就出现了Type接口来作为Java编程语言中所有类型的公共接口。有了Type之后,Class类作为Type的子类,仍然表示类、接口、简单类型和数组,而参数化类型、类型变量类型和泛型数组类型,分别由ParameterizedType、TypeVariable和GenericArrayType这几个子接口来表示。另外还有一个WildcardType,它表示通配符表达式。这些子接口如下图所示:
下面这个表格总结出了这些子接口与java语言中类型之间的映射关系:
java语言类型 | 对应的Type类 |
---|---|
简单类型 | Class |
类(raw type) | Class |
接口(raw type) | Class |
数组 | Class |
参数化类型(泛型类、泛型接口) | ParameterizedType |
类型变量 | TypeVariable |
泛型数组 | GenericArrayType |
理解类型变量和参数化类型
在逐一介绍这几个接口之前,我们需要先理解类型变量和参数化类型是什么,我们结合《java语言规范》第八版中的定义来看:
类型变量是在类、接口、方法和构造器中用作类型的非限定标识符。
类型变量可以声明为泛化类声明、泛化接口声明、泛化方法声明和泛化构造器声明中的类型参数。
看这个例子:
public class TypeVariableTest1<T> {
}
public class TypeVariableTest2<E extends SimpleObject> {
}
其中,T和E这两个非限定标识符就是类型变量。
参数化类型是形式为C
上面例子中的TypeVariableTest1
结合实例,理解类型变量和参数化类型应该是比较容易的。
Type的定义
Type的声明如下:
/**
* Type is the common superinterface for all types in the Java
* programming language. These include raw types, parameterized types,
* array types, type variables and primitive types.
*
* @since 1.5
*/
public interface Type {
default String getTypeName() {
return toString();
}
}
注释中的解释跟我们的理解基本一致。
Type接口只有一个方法getTypeName,默认实现是调用toString方法。
TypeVariable接口
接口声明与含义
TypeVariable接口就用来表示所有的类型变量。
它的声明如下:
public interface TypeVariable<D extends GenericDeclaration>
extends Type, AnnotatedElement {}
从声明中我们就能知道很多东西:首先,继承Type类,这个不用再阐述,类型变量也是java类型中的一种。其次,继承了AnnotatedElement接口,说明是可以被注解的,在java中,注解可以用在类型变量上面,最后,它自己也有类型变量,D extends GenericDeclaration,用来指定声明这个类型变量的元素的类型,例如是类、接口方法还是构造方法,我们知道,只有这四个能声明类型变量,而GenericDeclaration正好是这几个的父接口。
getBounds()方法
TypeVariable提供了四个方法,我们来逐个分析一下:
Type[] getBounds();
getBounds方法用来获取类型变量的上边界,如果类型变量的上边界没有明确声明,就会返回Object,不管上边界是什么,肯定是一个类型(类、接口、类型变量、参数化类型),所以返回值是Type的数组。
看下面的例子:
public class TypeVariableTest<T,E extends SimpleObject,F extends E> {
public static void main(String[] args) {
TypeVariable<Class<TypeVariableTest>>[] typeParameters = TypeVariableTest.class.getTypeParameters();
for (TypeVariable<Class<TypeVariableTest>> typeParameter : typeParameters) {
System.out.println(Arrays.toString(typeParameter.getBounds()));
}
}
}
在讲GenericDeclaration这个接口时,我们知道该接口唯一提供的方法getTypeParameters方法就是用来获取这个GenericDeclaration(类、方法或者构造方法)声明的TypeVariable数组,所以这个示例中我们用这个方法来得到了TypeVariableTest这个类的类型变量数组。
输出:
[class java.lang.Object]
[class person.andy.concurrency.classload.SimpleObject]
[E]
结果与注释中的说明一样,对于没有显式声明上边界的,返回的是Object,对于显式声明上边界的,直接返回上边界,类型变量F的上边界是E,这里直接输出E。
getGenericDeclaration()方法
D getGenericDeclaration();
这个方法返回声明这个类型变量的GenericDeclaration,如果一个Class声明了这个类型变量,就返回这个Class,如果一个Method声明了这个类型变量,就返回这个Method,看下面的例子:
public class TypeVariableTest<T,E extends SimpleObject,F extends E> {
public <G extends SimpleObject> void test(G g){
}
public static void main(String[] args) throws NoSuchMethodException {
TypeVariable<Class<TypeVariableTest>>[] typeParameters = TypeVariableTest.class.getTypeParameters();
for (TypeVariable<Class<TypeVariableTest>> typeParameter : typeParameters) {
System.out.println(typeParameter.getGenericDeclaration());
}
TypeVariableTest<String,SimpleObject,SimpleObject> typeVariableTest = new TypeVariableTest<>();
Method test = typeVariableTest.getClass().getDeclaredMethod("test", SimpleObject.class);
TypeVariable<Method>[] methodTypeParameters = test.getTypeParameters();
for (TypeVariable<Method> methodTypeParameter : methodTypeParameters) {
System.out.println(methodTypeParameter.getGenericDeclaration());
}
}
}
输出:
class person.andy.concurrency.reflect.TypeVariableTest
class person.andy.concurrency.reflect.TypeVariableTest
class person.andy.concurrency.reflect.TypeVariableTest
public void person.andy.concurrency.reflect.TypeVariableTest.test(person.andy.concurrency.classload.SimpleObject)
我们对声明了类型变量的类和方法分别调用了getTypeParameters方法获取类型变量,然后调用getGenericDeclaration方法,返回了对应的Class和Method。
getName()方法
getName()方法返回类型变量的名称,与源代码中的名称一样。看下面的例子:
public class TypeVariableTest<T,E extends SimpleObject,F extends E> {
public static void main(String[] args) throws NoSuchMethodException {
TypeVariable<Class<TypeVariableTest>>[] typeParameters = TypeVariableTest.class.getTypeParameters();
for (TypeVariable<Class<TypeVariableTest>> typeParameter : typeParameters) {
System.out.println(typeParameter.getName());
}
}
}
返回的直接就是:
T
E
F
ParameterizedType接口
接口声明与含义
ParameterizedType的定义如下:
public interface ParameterizedType extends Type {}
ParameterizedType用来表示所有的参数化类型(泛型类、泛型接口)。
getActualTypeArguments()方法
Type[] getActualTypeArguments();
这个方法返回一个Type数组,数组里面的每个Type表示一个这个泛型类、泛型接口的类型参数。
类型参数可以这样解释:如果类声明了一个或者多个类型变量,那么它就是泛化的,这些类型变量被称为该类的类型参数。也就是说,类型参数就是泛型类或泛型接口中的类型变量。
看下面的例子:
public class ParameterizedTypeTest<T,E> {
public static void main(String[] args) {
Class<ParameterizedTypeTestSub> parameterizedTypeTestClass = ParameterizedTypeTestSub.class;
Type genericSuperclass = parameterizedTypeTestClass.getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
class ParameterizedTypeTestSub extends ParameterizedTypeTest<String, SimpleObject> {
}
Class类的getGenericSuperclass()方法会返回Class代表的类的参数化的父类,这里就是ParameterizedTypeTest
class java.lang.String
class person.andy.concurrency.classload.SimpleObject
getRawType()方法
这个方法返回这个参数化类型(泛型类、泛型接口)的原生类型(raw type)。这个应该很好理解,看下面的例子:
public class ParameterizedTypeTest<T,E> {
public static void main(String[] args) {
Class<ParameterizedTypeTestSub> parameterizedTypeTestClass = ParameterizedTypeTestSub.class;
Type genericSuperclass = parameterizedTypeTestClass.getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
System.out.println(parameterizedType.getRawType());
}
}
class ParameterizedTypeTestSub extends ParameterizedTypeTest<String, SimpleObject> {
}
输出:
class person.andy.concurrency.reflect.type.ParameterizedTypeTest
输出的就是ParameterizedTypeTest这个原生类型。
getOwnerType()方法
这个方法返回这个参数化类型作为member所属的类型。例如,如果当前参数化类型是o,会返回0
public class ParameterizedTypeTest<T,E> {
public Map.Entry<String,Object> entry;
public static void main(String[] args) throws NoSuchFieldException {
Class<ParameterizedTypeTest> parameterizedTypeTestClass1 = ParameterizedTypeTest.class;
Field entry = parameterizedTypeTestClass1.getDeclaredField("entry");
ParameterizedType genericType = (ParameterizedType) entry.getGenericType();
System.out.println(genericType.getOwnerType());
}
}
输出:
interface java.util.Map
因为Entry是在Map中定义的,所以返回它的所有者就是Map。
小结
泛型的出现使得一个Class不能表示所有的类,类型变量、参数化类型、泛型数组,这些新的类型(type)同样需要在反射中用到,比如,我们可能想获取某个泛型类的类型参数。这样就出现了Type接口,这个接口作为java编程语言中所有类型的公共接口,不同的子类分别代表不同的类型。
除了Class,最重要的就是TypeVariable(类型变量)和ParameterizedType(参数化类型)这两个,它们分别提供了一些方法来获取对应类型特性相关的信息。
之后,我们会在反射中继续看到这些接口的用处。