摘要
JDK:1.8.0_202
# 一:类信息
Java程序中的许多对象在运行时都会出现两种类型:编译时类型和运行时类型,例如代码:Person p=new Student()
,这行代码将会生成一个p变量,该变量的编译时类型为Person,运行时类型为Student;除此之外,还有更极端的情形,程序在运行时接收到外部传入的一个对象,该对象的编译时类型是Object,但程序又需要调用该对象运行时类型的方法。
为了解决这些问题,程序需要在运行时发现对象和类的真实信息。解决该问题有以下两种做法。
第一种做法是假设在编译时和运行时都完全知道类型的具体信息,在这种情况下,可以先使用
instanceof
运算符进行判断,再利用强制类型转换将其转换成其运行时类型的变量即可。第二种做法是编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。
# 1.1 获取 Class 对象
每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到 JVM 中的这个类。在Java程序中获得Class对象通常有如下三种方式。
- 使用Class类的
forName(String clazzName)
静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名); - 调用某个类的class属性来获取该类对应的Class对象。例如,
Person.class
将会返回Person类对应的Class对象; - 调用某个对象的
getClass()
方法。该方法是java.lang.Object类中的一个方法,所以所有的Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。
对于第一种方式和第二种方式都是直接根据类来取得该类的Class对象,相比之下,第二种方式有如下两种优势。
- 代码更安全。程序在编译阶段就可以检查需要访问的Class对象是否存在;
- 程序性能更好。因为这种方式无须调用方法,所以性能更好。
# 1.2 从 Class 中获取信息
Class类提供了大量的实例方法来获取该 Class 对象所对应类的详细信息,Class 类大致包含如下方法,下面每个方法都可能包括多个重载的版本:
下面4个方法用于获取Class对应类所包含的构造器:
Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
:返回此Class对象对应类的、带指定形参列表的public构造器。Constructor<?>[] getConstructors() throws SecurityException
:返回此Class对象对应类的所有public构造器。Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
:返回此Class对象对应类的、带指定形参列表的构造器,与构造器的访问权限无关。Constructor<?>[] getDeclaredConstructors() throws SecurityException
:返回此Class对象对应类的所有构造器,与构造器的访问权限无关。
下面4个方法用于获取Class对应类所包含的方法:
Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
:返回此Class对象对应类的、带指定形参列表的public方法。Method[] getMethods() throws SecurityException
:返回此Class对象所表示的类的所有public方法。Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
:返回此Class对象对应类的、带指定形参列表的方法,与方法的访问权限无关。Method[] getDeclaredMethods() throws SecurityException
:返回此Class对象对应类的全部方法,与方法的访问权限无关。
如下4个方法用于访问Class对应类所包含的成员变量:
Field getField(String name) throws NoSuchFieldException, SecurityException
:返回此Class对象对应类的、指定名称的public成员变量。Field[] getFields() throws SecurityException
:返回此Class对象对应类的所有public成员变量。Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
:返回此Class对象对应类的、指定名称的成员变量,与成员变量的访问权限无关。Field[] getDeclaredFields() throws SecurityException
:返回此Class对象对应类的全部成员变量,与成员变量的访问权限无关。
如下几个方法用于访问Class对应类上所包含的Annotation:
<A extends Annotation> A getAnnotation(Class<A> annotationClass)
:尝试获取该Class对象对应类上存在的、指定类型的Annotation;如果该类型的注解不存在,则返回null。<A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)
:这是Java 8新增的方法,该方法尝试获取直接修饰该Class对象对应类的、指定类型的Annotation;如果该类型的注解不存在,则返回null。Annotation[] getAnnotations()
:返回修饰该Class对象对应类上存在的所有Annotation。boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
:判断该程序元素上是否存在指定类型的注解,如果存在则返回true,否则返回false。Annotation[] getDeclaredAnnotations()
:返回直接修饰该Class对应类的所有Annotation。<A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass)
:该方法的功能与前面介绍的getAnnotation() 方法基本相似。但由于Java 8增加了重复注解功能,因此需要使用该方法获取修饰该类的、指定类型的多个Annotation。<A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass)
:该方法的功能与前面介绍的 getDeclaredAnnotations() 方法基本相似。但由于Java 8增加了重复注解功能,因此需要使用该方法获取直接修饰该类的、指定类型的多个Annotation。
如下方法用于访问该Class对象对应类包含的内部类:
Class<?>[] getDeclaredClasses() throws SecurityException
:返回该Class对象对应类里包含的全部内部类。
如下方法用于访问该Class对象对应类所在的外部类:
Class<?> getDeclaringClass() throws SecurityException
:返回该Class对象对应类所在的外部类。
如下方法用于访问该Class对象对应类所实现的接口:
Class<?>[] getInterfaces()
:返回该Class对象对应类所实现的全部接口。
如下几个方法用于访问该Class对象对应类所继承的父类:
native Class<? super T> getSuperclass()
:返回该 Class对象对应类的超类的Class对象。
如下方法用于获取Class对象对应类的修饰符、所在包、类名等基本信息:
native int getModifiers()
:返回此类或接口的所有修饰符。修饰符由 public、protected、private、final、static、abstract 等对应的常量组成,返回的整数应使用Modifier工具类的方法来解码,才可以获取真实的修饰符。Package getPackage()
:获取此类的包。String getName()
:以字符串形式返回此Class对象所表示的类的名称。String getSimpleName()
:以字符串形式返回此Class对象所表示的类的简称。
除此之外,Class对象还可调用如下几个判断方法来判断该类是否为接口、枚举、注解类型等:
boolean isAnnotation()
:返回此Class对象是否表示一个注解类型(由@interface定义)。boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
:判断此 Class 对象是否使用了Annotation修饰。boolean isAnonymousClass()
:返回此Class对象是否是一个匿名类。native boolean isArray()
:返回此Class对象是否表示一个数组类。boolean isEnum()
:返回此Class对象是否表示一个枚举(由enum关键字定义)。native boolean isInterface()
:返回此Class对象是否表示一个接口(使用interface定义)。native boolean isInstance(Object obj)
:判断obj是否是此Class对象的实例,该方法可以完全代替 instanceof 操作符。
上面的多个 getMethod() 方法和 getConstructor() 方法中,都需要传入多个类型为
Class<?>
的参数,用于获取指定的方法或指定的构造器。关于这个参数的作用,假设某个类内包含如下三个info方法签名。public void info()
public void info(String str)
public void info(String str, Integer num)
这三个同名方法属于重载,它们的方法名相同,但参数列表不同。在 Java 语言中要确定一个方法光有方法名是不行的,如果仅仅只指定info方法——实际上可以是上面三个方法中的任意一个!如果需要确定一个方法,则应该由方法名和形参列表来确定,但形参名没有任何实际意义,所以只能由形参类型来确定。例如想指定第二个info方法,则必须指定方法名为info,形参列表为String.class——因此在程序中获取该方法使用如下代码:
// 前一个参数指定方法名,后面的个数可变的 Class 参数指定形参类型列表 clazz.getMethod("info", String.class)
1
2如果需要获取第三个 info 方法,则使用如下代码:
clazz.getMethod("info", String.class, Integer.class)
获取构造器时无须传入构造器名——同一个类的所有构造器的名字都是相同的,所以要确定一个构造器只要指定形参列表即可。
import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
// 定义可重复注解
@Repeatable(Annos.class)
@interface Anno {
}
@Retention(value = RetentionPolicy.RUNTIME)
@interface Annos {
Anno[] value();
}
// 使用 4 个注解修饰该类
@SuppressWarnings(value = "unchecked")
@Deprecated
// 使用重复注解修饰该类
@Anno
@Anno
public class ClassTest {
// 为该类定义一个私有的构造器
private ClassTest() {
}
// 定义一个有参数的构造器
public ClassTest(String name) {
System.out.println("执行有参数的构造器");
}
// 定义一个无参数的 info 方法
public void info() {
System.out.println("执行无参数的 info 方法");
}
// 定义一个有参数的 info 方法
public void info(String str) {
System.out.println("执行有参数的 info 方法,其 str 参数值:" + str);
}
// 定义一个测试用的内部类
class Inner {
}
public static void main(String[] args) throws Exception {
// 下面代码可以获取 ClassTest 对应的 Class
Class<ClassTest> clazz = ClassTest.class;
// 获取该 Class 对象所对应类的全部构造器
Constructor<?>[] ctors = clazz.getDeclaredConstructors();
System.out.println("ClassTest 的全部构造器如下:");
for (Constructor<?> c : ctors) {
System.out.println(c);
}
// 获取该 Class 对象所对应类的全部 public 构造器
Constructor<?>[] publicCtors = clazz.getConstructors();
System.out.println("ClassTest 的全部 public 构造器如下:");
for (Constructor<?> c : publicCtors) {
System.out.println(c);
}
// 获取该 Class 对象所对应类的全部 public 方法
Method[] mtds = clazz.getMethods();
System.out.println("ClassTest 的全部 public 方法如下:");
for (Method md : mtds) {
System.out.println(md);
}
// 获取该 Class 对象所对应类的指定方法
System.out.println("ClassTest 里带一个字符串参数的 info 方法为:" + clazz.getMethod("info", String.class));
// 获取该 Class 对象所对应类的全部注解
Annotation[] anns = clazz.getAnnotations();
System.out.println("ClassTest 的全部 Annotation 如下:");
for (Annotation an : anns) {
System.out.println(an);
}
System.out.println("该 Class 元素上的 @SuppressWarnings 注解为:" + Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class)));
System.out.println("该 Class 元素上的 @Anno 注解为:" + Arrays.toString(clazz.getAnnotationsByType(Anno.class)));
// 获取该 Class 对象所对应类的全部内部类
Class<?>[] inners = clazz.getDeclaredClasses();
System.out.println("ClassTest 的全部内部类如下:");
for (Class<?> c : inners) {
System.out.println(c);
}
// 使用 Class.forName() 方法加载 ClassTest 的 Inner 内部类
Class<?> inClazz = Class.forName("top.qform.ClassTest$Inner");
// 通过 getDeclaringClass() 访问该类所在的外部类
System.out.println("inClazz 对应类的外部类为:" + inClazz.getDeclaringClass());
System.out.println("ClassTest 的包为:" + clazz.getPackage());
System.out.println("ClassTest 的父类为:" + clazz.getSuperclass());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
虽然定义 ClassTest 类时使用了 @SuppressWarnings
注解,但程序运行时无法分析出该类里包含的该注解,这是因为 @SuppressWarnings 使用了 @Retention(value=SOURCE)
修饰,这表明 @SuppressWarnings 只能保存在源代码级别上,而通过 ClassTest.class 获取该类的运行时 Class 对象,所以程序无法访问到 @SuppressWarnings 注解。
对于只能在源代码上保留的注解,使用运行时获得的 Class 对象无法访问到该注解对象。
# 1.3 Java8 新增的方法参数反射
Java 8在java.lang.reflect包下新增了一个 Executable
抽象基类,该对象代表可执行的类成员,该类派生了 Constructor、Method
两个子类。
Executable
基类提供了大量方法来获取修饰该方法或构造器的注解信息;还提供了 isVarArgs()
方法用于判断该方法或构造器是否包含数量可变的形参,以及通过 getModifiers()
方法来获取该方法或构造器的修饰符。除此之外,Executable 提供了如下两个方法来获取该方法或参数的形参个数及形参名。
int getParameterCount()
:获取该构造器或方法的形参个数;Parameter[] getParameters()
:获取该构造器或方法的所有形参。
上面第二个方法返回了一个 Parameter[] 数组,Parameter 也是Java 8新增的API,每个 Parameter 对象代表方法或构造器的一个参数。Parameter
也提供了大量方法来获取声明该参数的泛型信息,还提供了如下常用方法来获取参数信息。
int getModifiers()
:获取修饰该形参的修饰符;String getName()
:获取形参名;Type getParameterizedType()
:获取带泛型的形参类型;Class<?> getType()
:获取形参类型;boolean isNamePresent()
:该方法返回该类的class文件中是否包含了方法的形参名信息;boolean isVarArgs()
:该方法用于判断该参数是否为个数可变的形参。
需要指出的是,使用javac命令编译Java源文件时,默认生成的class文件并不包含方法的形参名信息,因此调用 isNamePresent()
方法将会返回false,调用 getName()
方法也不能得到该参数的形参名。如果希望javac命令编译Java源文件时可以保留形参信息,则需要为该命令指定 -parameters
选项。
class Test {
public void replace(String str, List<String> list) {
}
}
public class MethodParameterTest {
public static void main(String[] args) throws Exception {
// 获取 String 的类
Class<Test> clazz = Test.class;
// 获取 String 类的带两个参数的 replace() 方法
Method replace = clazz.getMethod("replace", String.class, List.class);
// 获取指定方法的参数个数
System.out.println("replace 方法参数个数:" + replace.getParameterCount());
// 获取 replace 的所有参数信息
Parameter[] parameters = replace.getParameters();
int index = 1;
// 遍历所有参数
for (Parameter p : parameters) {
if (p.isNamePresent()) {
System.out.println("---第" + index++ + "个参数信息---");
System.out.println("参数名:" + p.getName());
System.out.println("形参类型:" + p.getType());
System.out.println("泛型类型:" + p.getParameterizedType());
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
如果现在 idea 编译添加 -parameters
可以查看 IDEA - 添加编译参数
# 二:反射操作对象
Class对象可以获得该类里的方法(由Method对象表示)、构造器(由Constructor对象表示)、成员变量(由Field对象表示),这三个类都位于 java.lang.reflect 包下,并实现了 java.lang.reflect.Member 接口。程序可以通过Method对象来执行对应的方法,通过Constructor对象来调用对应的构造器创建实例,能通过Field对象直接访问并修改对象的成员变量值。
# 2.1 创建对象
通过反射来生成对象有如下两种方式。
使用Class对象的
newInstance()
方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行 newInstance() 方法时实际上是利用默认构造器来创建该类的实例。先使用Class对象获取指定的
Constructor
对象,再调用Constructor对象的newInstance()
方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。
class Student {
public String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
@Override
public String toString() {
return Optional.ofNullable(name).isPresent() ? "有参构造器获取的实例" : "无参构造器获取的实例";
}
}
public class ObjectTest {
public static void main(String[] args) throws Exception {
// 方式二
// 根据字符串来获取对应的 Class 对象
Class<?> c1 = Class.forName("top.qform.reflect.Student");
// 使用 Class 对应类的无参构造器获取实例
Student s1 = (Student) c1.newInstance();
System.out.println(s1);
// 方式二
// 需要如下三个步骤
// 步骤一:获取该类的 Class 对象
Class<?> c2 = Class.forName("top.qform.reflect.Student");
// 步骤二:利用 Class 对象的 getConstructor() 方法来获取指定的构造器
Constructor<?> ctor = c2.getConstructor(String.class);
// 步骤三:调用 Constructor 的 newInstance() 方法来创建 Java 对象
Student s2 = (Student) ctor.newInstance("test");
System.out.println(s2);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 2.2 调用方法
当获得某个类对应的Class对象后,就可以通过该Class对象的 getMethods()
方法或者 getMethod()
方法来获取全部方法或指定方法——这两个方法的返回值是Method数组,或者Method对象。
每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用它对应的方法。在Method里包含一个 invoke()
方法,该方法的签名如下。
Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
:该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参。
class Employer {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class MethodTest {
public static void main(String[] args) throws Exception {
Employer employer = new Employer();
Class<?> clazz = employer.getClass();
// 获取希望调用的 setter 方法
Method mtd = clazz.getMethod("setName", String.class);
// 通过 Method 的 invoke 方法执行 setter 方法
mtd.invoke(employer, "test");
// 调用 getter 方法
System.out.println("getName返回值:" + clazz.getMethod("getName").invoke(employer));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
当通过 Method 的 invoke() 方法来调用对应的方法时,Java会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的 private 方法,则可以先调用Method对象的如下方法。
void setAccessible(boolean flag) throws SecurityException
:将Method对象的accessible
设置为指定的布尔值。值为true,指示该Method在使用时应该取消Java语言的访问权限检查;值为false,则指示该Method在使用时要实施Java语言的访问权限检查。
setAccessible()
方法并不属于Method,而是属于它的父类AccessibleObject。因此 Method、Constructor、Field
都可调用该方法,从而实现通过反射来调用private方法、private构造器和private成员变量。
# 2.3 访问成员变量
通过Class对象的 getFields()
或 getField()
方法可以获取该类所包括的全部成员变量或指定成员变量。Field提供了如下两组方法来读取或设置成员变量值。
getXxx(Object obj)
:获取obj对象的该成员变量的值。此处的Xxx对应8种基本类型,如果该成员变量的类型是引用类型,则取消get后面的Xxx。setXxx(Object obj,Xxx val)
:将obj对象的该成员变量设置成val值。此处的Xxx对应8种基本类型,如果该成员变量的类型是引用类型,则取消set后面的Xxx。
class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
public class FieldTest {
public static void main(String[] args) throws Exception {
// 创建一个 Person 对象
Person p = new Person();
// 获取 Person 类对应的 Class 对象
Class<Person> personClass = Person.class;
// 获取 Person 的名为 name 的成员变量
// 使用 getDeclaredField() 方法表明可获取各种访问控制符的成员变量
Field nameField = personClass.getDeclaredField("name");
// 设置通过反射访问该成员变量时取消访问权限检查
nameField.setAccessible(true);
// 调用 set() 方法为 p 对象的 name 成员变量设置值
nameField.set(p, "张三");
// 获取 Person 类名为 age 的成员变量
Field ageField = personClass.getDeclaredField("age");
// 设置通过反射访问该成员变量时取消访问权限检查
ageField.setAccessible(true);
// 调用 setInt() 方法为 p 对象的 age 成员变量设置值
ageField.setInt(p, 30);
System.out.println(p);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
getField()
方法只能获取public访问控制的成员变量,而getDeclaredField()
方法则可以获取所有的成员变量
# 2.4 操作数组
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array来动态地创建数组,操作数组元素等。Array提供了如下几类方法。
static Object newInstance(Class<?>componentType, int...length)
:创建一个具有指定的元素类型、指定维度的新数组。static xxx getXxx(Object array, int index)
:返回array数组中第index个元素。其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变为 get(Object array, int index)。static void setXxx(Object array, int index, xxx val)
:将array数组中第 index 个元素的值设为val。其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变成 set(Object array, int index, Object val)。
public class ArrayTest {
public static void main(String[] args) throws Exception {
// 创建一个元素类型为 String,长度为 10 的数组
Object arr1 = Array.newInstance(String.class, 10);
// 依次为 arr 数组中 index 为 5、6 的元素赋值
Array.set(arr1, 5, "下标5");
Array.set(arr1, 6, "下标6");
// 取出 index 为 5 的元素的值
Object i5 = Array.get(arr1, 5);
System.out.println(i5);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2.5 获取泛型信息
通过指定类对应的 Class 对象,可以获得该类里包含的所有成员变量,不管该成员变量是使用 private 修饰,还是使用 public 修饰。获得了成员变量对应的 Field 对象后,就可以很容易地获得该成员变量的数据类型,即使用如下代码即可获得指定成员变量的类型。
// 获取成员变量 f 的类型
Class<?> a = f.getType();
但这种方式只对普通类型的成员变量有效。如果该成员变量的类型是有泛型类型的类型,如 Map<String, Integer>
类型,则不能准确地得到该成员变量的泛型参数。为了获得指定成员变量的泛型类型,应先使用如下方法来获取该成员变量的泛型类型。
// 获取成员变量 f 的泛型类型
Type gType = f.getGenericType();
然后将 Type
对象强制类型转换为 ParameterizedType
对象,ParameterizedType 代表被参数化的类型,也就是增加了泛型限制的类型。ParameterizedType 类提供了如下两个方法。
Type getRawType()
:返回没有泛型信息的原始类型;Type[] getActualTypeArguments()
:返回泛型参数的类型。
public class GenericTest {
private Map<String, Integer> score;
public static void main(String[] args) throws Exception {
Class<GenericTest> clazz = GenericTest.class;
Field f = clazz.getDeclaredField("score");
// 直接使用 getType() 取出类型只对普通类型的成员变量有效
Class<?> a = f.getType();
System.out.println("score的类型是:" + a);
// 获取成员变量 f 的泛型类型
Type gType = f.getGenericType();
// 如果 gType 类型是 ParameterizedType 对象
if (gType instanceof ParameterizedType) {
// 强制类型转换
ParameterizedType pType = (ParameterizedType) gType;
// 获取原始类型
Type rType = pType.getRawType();
System.out.println("原始类型是:" + rType);
// 取得泛型类型的泛型参数
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("泛型信息是:");
for (int i = 0; i < tArgs.length; i++) {
System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
从上面的运行结果可以看出,使用 getType()
方法只能获取普通类型的成员变量的数据类型;对于增加了泛型的成员变量,应该使用 getGenericType()
方法来取得其类型。
Type也是java.lang.reflect包下的一个接口,该接口代表所有类型的公共高级接口,Class是Type接口的实现类。Type包括原始类型、参数化类型、数组类型、类型变量和基本类型等。
# 三:参考文献
- 《疯狂Java讲义(第三版) - 李刚》