一、概述
Java反射機制定義
Java反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類中的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制。枚舉除外
** 反射機制的功能**
- 在運行時判斷任意一個對象所屬的類。
在運行時構(gòu)造任意一個類的對象。
在運行時判斷任意一個類所具有的成員變量和方法。
在運行時調(diào)用任意一個對象的方法。
-
生成動態(tài)代理。
import java.util.Date;
public class ReflactDemo_01 {
public static void main(String[] args) throws Exception {
/**
需求1:通過obj對象,調(diào)用java.util.Date類中的toLocaleString方法.
Object obj =new java.util.Date();
java.util.Date d = (java.util.Date) obj;
d.toLocaleString(); (過時了 )
// 創(chuàng)建Class對象 如何來表示一個字節(jié)碼對象// 需求2: 獲取java.util.Date類的字節(jié)碼對象
// 方式1.使用class屬性
Class<java.util.Date> clz1 = java.util.Date.class;// 方式2,通過對象的getClass方法來獲取,getClass是Object類中的方法
java.util.Date date = new java.util.Date();
Class<?> clz2 = date.getClass();// 方式3,通過Class類中的靜態(tài)方法forName(String className);
Class<?> clz3 = Class.forName("java.util.Date");
/
/*- 所有的數(shù)據(jù)類型都有class屬性. Class clz = 數(shù)據(jù)類型.class; 九大內(nèi)置Class實例:
- JVM中預(yù)先提供好的Class實例,
分別:byte,short,int,long,float,double,boolea,char,void. - 表示:byte.class,short.class,int.class,....void.class.
/
/* - 在8大基本數(shù)據(jù)類型的包裝類中,都有一個常量:TYPE,用于返回該包裝類對應(yīng)基本類的字節(jié)碼對象.
System.out.println(Integer.TYPE == int.class);//true - 注意:Integer和int是不同的數(shù)據(jù)類型
System.out.println(Integer.class == int.class);//false
/
// System.out.println(Integer.TYPE == int.class);
// System.out.println(Integer.class == int.class);
/* - 如何來表示數(shù)組的Class實例. 方式1: 數(shù)組類型.class; 方式2: 數(shù)組對象.getClass();
- 注意:所有的具有相同的維數(shù)和相同元素類型的數(shù)組共享同一份字節(jié)碼對象,和元素沒有關(guān)系.
/
/* - Class<?> clz4 = int[].class;
System.out.println(clz4); String[] arr ={ "1", "3", "e" };
Class<?> clz5 = arr.getClass(); - System.out.println(clz5);
*/
}
}
**通過反射獲取構(gòu)造器 **
import java.lang.reflect.Constructor;
/**
- 構(gòu)造器最大的作用:創(chuàng)建對象. 為什么使用反射創(chuàng)建對象,為什么不直接來new呢? 在框架中,提供給我們的都是字符串.
- 使用反射創(chuàng)建對象: 步驟:
- 1);找到構(gòu)造器所在類的字節(jié)碼對象. 2):獲取構(gòu)造器對象. 3):使用 反射,創(chuàng)建對象
- Constructor<T>類:表示類中構(gòu)造器的類型,Constructor的實例就是某一個類中的某一個構(gòu)造器 常用方法:
- public T newInstance(Object... initargs):如調(diào)用帶參數(shù)的構(gòu)造器,只能使用該方式. 參數(shù):initargs:表示調(diào)用構(gòu)造器的實際參數(shù)
- 返回:返回創(chuàng)建的實例,T表示Class所表示類的類型
- 如果:一個類中的構(gòu)造器是外界可以直接訪問,同時沒有參數(shù).,那么可以直接使用Class類中的newInstance方法創(chuàng)建對象.
- public Object newInstance():相當(dāng)于new 類名(); 調(diào)用私有的構(gòu)造器:
*/
class User {
String name;
int age;
// create construct
public User() {
}
public User(String name) {
this.name = name;
}
private User(String name, int age) {
this.name = name;
this.age = age;
}
// 需求:通過反射來獲取某一個類的構(gòu)造器:
// 1):獲取該類的字節(jié)碼對象.
// 2):從該字節(jié)碼對象中去找需要獲取的構(gòu)造器.
// 獲取指定的構(gòu)造器
public static void getOne() throws Exception {
// 獲取構(gòu)造器所在類的字節(jié)碼對象
Class<User> clz = User.class;
// 獲取clz對象中的所有構(gòu)造器
// 需求1.獲取public User()
Constructor<User> con = clz.getConstructor();
System.out.println(con);// 需求2.獲取public User(String name) con = clz.getDeclaredConstructor(String.class); System.out.println(con); // 3.獲取private User(String name, int age) con = clz.getDeclaredConstructor(String.class, int.class); System.out.println(con);
}
// 獲取所有的構(gòu)造器
public static void getAll() {
// 獲取構(gòu)造器所在類的字節(jié)碼
Class<User> clz1 = User.class;
// 獲取clz1對象中所有的構(gòu)造器
Constructor<?>[] con = clz1.getConstructors();
System.out.println(con.length);// 2Constructor<?>[] cons = clz1.getDeclaredConstructors(); System.out.println(cons.length);// 3 for (Constructor<?> c : cons) { System.out.println(c); } }
}
public class ReflactDemo_02 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
// User.getOne();
User.getAll();
}
}
通過反射來獲取某一個類的構(gòu)造器
需求:通過反射來獲取某一個類的構(gòu)造器:
1):獲取該類的字節(jié)碼對象.
2)從該字節(jié)碼對象中去找需要獲取的構(gòu)造器.
Class類獲取構(gòu)造器方法:
Constructor類:表示類中構(gòu)造器的類型,Constructor的實例就是某一個類中的某一個構(gòu)造器
public Constructor<?>[] getConstructors():該方法只能獲取當(dāng)前Class所表示類的public修飾的構(gòu)造器
public Constructor<?>[] getDeclaredConstructors():獲取當(dāng)前Class所表示類的所有的構(gòu)造器,和訪問權(quán)限無關(guān)
public Constructor<T> getConstructor(Class<?>... parameterTypes)
注意獲取當(dāng)前Class所表示類中指定的一個public的構(gòu)造器
參數(shù):parameterTypes表示:構(gòu)造器參數(shù)的Class類型
如:
public User(String name)
Constructor c = clz.getConstructor(String.class);
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
獲取當(dāng)前Class所表示類中指定的一個的構(gòu)造
訪問私有的成員: 必須先設(shè)置可訪問的 對象setAccessible(true);
import java.lang.reflect.Constructor;
class User1 {
String name;
int age;
// create constructor
public User1() {
System.out.println("無參構(gòu)造器");
}
public User1(String name) {
this.name = name;
System.out.println("構(gòu)造器" + name);
}
private User1(String name, int age) {
this.name = name;
this.age = age;
System.out.println("構(gòu)造器" + name + age);
}
}
public class ReflactDemo_03 {
public static void main(String[] args) throws Exception {
// 傳統(tǒng)的方式創(chuàng)建對象
// new User1();
// new User1("CoderZS");
// 獲取類的字節(jié)碼
Class<User1> clz = User1.class;
// 獲取public User1() 注意有public 修飾的 一定與方法相匹配
Constructor<User1> con = clz.getConstructor();
// 用構(gòu)造器的newInstance方法來創(chuàng)建對象 會調(diào)用構(gòu)造器的方法
con.newInstance();
System.out.println(con);
// 調(diào)用public User1(String name)
con = clz.getConstructor(String.class);
con.newInstance("CoderZS");
// 調(diào)用 private User1(String name,int age)!!!!!!!!!!!!!!
con = clz.getDeclaredConstructor(String.class, int.class);
// 設(shè)置當(dāng)前構(gòu)造器可以訪問
// 訪問私有的成員:必須先設(shè)置可訪問的對象
// setAccessible(true);
con.setAccessible(true);
con.newInstance("CoderZS", 18);
}
}
使用反射獲取方法
import java.lang.reflect.Method;
/**
* 需求:使用反射獲取類中的方法: 1):獲取方法所在類的字節(jié)碼對象. 2):獲取方法.
* Class類中常用方法: public Method[] getMethods():獲取包括自身和繼承過來的所有的public方法
* public Method[] getDeclaredMethods():獲取自身類中所有的方法(不包括繼承的,和訪問權(quán)限無關(guān))
* public Method getMethod(String methodName, Class<?>...parameterTypes)
* :表示調(diào)用指定的一個公共的方法(包括繼承的) 參數(shù): methodName: 表示被調(diào)用方法的名字
* parameterTypes:表示被調(diào)用方法的參數(shù)的Class類型如String.class
* public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
* :表示調(diào)用指定的一個本類中的方法(不包括繼承的) 參數(shù): methodName: 表示被調(diào)用方法的名字
* parameterTypes:表示被調(diào)用方法的參數(shù)的Class類型如String.class
*/
class User2 {
public void doWork() {
}
public static void doWork(String name) {
}
private String doNotWork(String name, int age) {
return name + "有參有返回值" + age;
}
// 獲取User2類中指定的一個方法
static void getOne1() throws Exception {
// 獲取類的字節(jié)碼
Class<User2> clz = User2.class;
// 需求1:獲取doWork()
Method m = clz.getMethod("doWork");
System.out.println(m);
// 需求:獲取private String doNotWork(String name,int age)
m = clz.getDeclaredMethod("doNotWork", String.class, int.class);
System.out.println(m);
}
static void getAll() {
// 獲取類的字節(jié)碼
Class<User2> clz = User2.class;
// 需求1:獲取doWork()
Method[] ms = clz.getMethods();
System.out.println(ms);
System.out.println(ms.length);
for (Method m : ms) {
System.out.println(m);
}
}
}
// 使用反射獲取類中方法
public class ReflactDemo_04 {
public static void main(String[] args) throws Exception {
// User2.getOne1();
User2.getAll();
}
}
使用反射調(diào)用私有靜態(tài)方法(public-private--static)
import java.lang.reflect.Method;
/**
* 使用反射調(diào)用方法: 1):獲取方法所在類的字節(jié)碼對象. 2):獲取方法對象. 3):使用反射調(diào)用方法.
* 如何使用反射調(diào)用一個方法:
* 在Method類中有方法: public Object invoke(Object obj,Object...args):表示調(diào)用當(dāng)前Method所表示的方法
* 參數(shù): obj: 表示被調(diào)用方法底層所屬對象
* Method m =clz.getMethod("doWork",String.class);
* args:表示調(diào)用方法是傳遞的實際參數(shù) 返回: 底層方法的返回結(jié)果
* 調(diào)用私有方法: 在調(diào)用私有方法之前:應(yīng)該設(shè)置該方法為可訪問的 又因為Method是AccessibleObject子類,所以Method中具有該方法.
* doWorkMethod.setAccessible(true);
*/
class User3 {
public void doWork() {
System.out.println("使用反射調(diào)用--無參--方法--doWork");
}
public void doWork(String name) {
System.out.println("使用反射調(diào)用--有參--方法---doWork");
}
private String doWork(String name, int age) {
return name + "使用反射調(diào)用--私有--方法" + age;
}
public static String doWork(String name, String sex, int age) {
return name + "使用反射調(diào)用--靜態(tài)--方法" + sex + age;
}
}
public class ReflactDemo_05 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
// 使用反射調(diào)用方法
// 需求1:調(diào)用public void doWork()
// 獲取類的字節(jié)碼
Class<User3> clz = User3.class;
// 獲取指定方法
Method m = clz.getMethod("doWork");
// 創(chuàng)建對象
Object result = m.invoke(clz.newInstance());
System.out.println(result);// null
// 需求2:public static void doWork(String name)
m = clz.getMethod("doWork", String.class);
result = m.invoke(clz.newInstance(), "CoderZS");
// 需求3:調(diào)用private String doNotWork(String name, int age)
m = clz.getDeclaredMethod("doWork", String.class, int.class);
m.setAccessible(true);
result = m.invoke(clz.newInstance(), "CoderZS", 18);
System.out.println(result);
// 需求4,使用反射調(diào)用靜態(tài)方法: 靜態(tài)方法不屬于任何對象,靜態(tài)方法屬于類本身.此時把invoke方法的第一個參數(shù)設(shè)置為null即可.
m = clz.getDeclaredMethod("doWork", String.class, String.class, int.class);
m.setAccessible(true);
result = m.invoke(null, "CoderZS", "boy", 18);
System.out.println(result);
}
}
概括
-
1.獲取class對象的成員變量
//獲取class對象的所有屬性 Field[] allFields = class1.getDeclaredFields(); //獲取class對象的public屬性 Field[] publicFields = class1.getFields(); //獲取class指定屬性 Field ageField = class1.getDeclaredField("age"); //獲取class指定的public屬性 Field desField = class1.getField("des");
2.獲取class對象的方法
//獲取class對象的所有聲明方法
Method[] methods = class1.getDeclaredMethods();
//獲取class對象的所有public方法 包括父類的方法
Method[] allMethods = class1.getMethods();
//返回次Class對象對應(yīng)類的、帶指定形參列表的public方法
Method method = class1.getMethod("info", String.class);
//返回次Class對象對應(yīng)類的、帶指定形參列表的方法
Method declaredMethod = class1.getDeclaredMethod("info", String.class);
-
3.獲取class對象的構(gòu)造函數(shù)
//獲取class對象的所有聲明構(gòu)造函數(shù) Constructor<?>[] allConstructors = class1.getDeclaredConstructors(); //獲取class對象public構(gòu)造函數(shù) Constructor<?>[] publicConstructors = class1.getConstructors(); //獲取指定聲明構(gòu)造函數(shù) Constructor<?> constructor = class1.getDeclaredConstructor(String.class); //獲取指定聲明的public構(gòu)造函數(shù) Constructor publicConstructor = class1.getConstructor(String.class);
4.其他方法
//獲取class對象的所有注解
Annotation[] annotations = (Annotation[]) class1.getAnnotations();
//獲取class對象指定注解
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);
//獲取class對象的直接超類的 Type
Type genericSuperclass = class1.getGenericSuperclass();
//獲取class對象的所有接口的type集合
Type[] interfaceTypes = class1.getGenericInterfaces();
獲取class對象的其他信息
boolean isPrimitive = class1.isPrimitive();//判斷是否是基礎(chǔ)類型
boolean isArray = class1.isArray();//判斷是否是集合類
boolean isAnnotation = class1.isAnnotation();//判斷是否是注解類
boolean isInterface = class1.isInterface();//判斷是否是接口類
boolean isEnum = class1.isEnum();//判斷是否是枚舉類
boolean isAnonymousClass = class1.isAnonymousClass();//判斷是否是匿名內(nèi)部類
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判斷是否被某個注解類修飾
String className = class1.getName();//獲取class名字 包含包名路徑
Package aPackage = class1.getPackage();//獲取class的包信息
String simpleName = class1.getSimpleName();//獲取class類名
int modifiers = class1.getModifiers();//獲取class訪問權(quán)限
Class<?>[] declaredClasses = class1.getDeclaredClasses();//內(nèi)部類
Class<?> declaringClass = class1.getDeclaringClass();//外部類
動態(tài)代理是指在運行時動態(tài)生成代理類。即,代理類的字節(jié)碼將在運行時生成并載入當(dāng)前代理的 ClassLoader。與靜態(tài)處理類相比,動態(tài)類有諸多好處。
- 不需要為(RealSubject )寫一個形式上完全一樣的封裝類,假如主題接口(Subject)中的方法很多,為每一個接口寫一個代理方法也很麻煩。如果接口有變動,則目標(biāo)對象和代理類都要修改,不利于系統(tǒng)維護;
- 使用一些動態(tài)代理的生成方法甚至可以在運行時制定代理類的執(zhí)行邏輯,從而大大提升系統(tǒng)的靈活性。
動態(tài)代理涉及的主要類
主要涉及兩個類,這兩個類都是java.lang.reflect包下的類,內(nèi)部主要通過反射來實現(xiàn)的。
java.lang.reflect.Proxy: 這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy 類。
Proxy提供了用戶創(chuàng)建動態(tài)代理類和代理對象的靜態(tài)方法,它是所有動態(tài)代理類的父類。
java.lang.reflect.InvocationHandler: 這里稱他為"調(diào)用處理器",它是一個接口。當(dāng)調(diào)用動態(tài)代理類中的方法時,將會直接轉(zhuǎn)接到執(zhí)行自定義的InvocationHandler中的invoke()方法。即我們動態(tài)生成的代理類需要完成的具體內(nèi)容需要自己定義一個類,而這個類必須實現(xiàn) InvocationHandler 接口,通過重寫invoke()方法來執(zhí)行具體內(nèi)容。
Proxy提供了如下兩個方法來創(chuàng)建動態(tài)代理類和動態(tài)代理實例。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回代理類的java.lang.Class對象。第一個參數(shù)是類加載器對象(即哪個類加載器來加載這個代理類到 JVM 的方法區(qū)),第二個參數(shù)是接口(表明你這個代理類需要實現(xiàn)哪些接口),第三個參數(shù)是調(diào)用處理器類實例(指定代理類中具體要干什么),該代理類將實現(xiàn)interfaces所指定的所有接口,執(zhí)行代理對象的每個方法時都會被替換執(zhí)行InvocationHandler對象的invoke方法。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回代理類實例。參數(shù)與上述方法一致。
對應(yīng)上述兩種方法創(chuàng)建動態(tài)代理對象的方式:
//創(chuàng)建一個InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy生成一個動態(tài)代理類
Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
//獲取proxyClass類中一個帶InvocationHandler參數(shù)的構(gòu)造器
Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
//調(diào)用constructor的newInstance方法來創(chuàng)建動態(tài)實例
RealSubject real = (RealSubject)constructor.newInstance(handler);
//創(chuàng)建一個InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy直接生成一個動態(tài)代理對象
RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
newProxyInstance這個方法實際上做了兩件事:第一,創(chuàng)建了一個新的類【代理類】,這個類實現(xiàn)了Class[] interfaces中的所有接口,并通過你指定的ClassLoader將生成的類的字節(jié)碼加載到JVM中,創(chuàng)建Class對象;第二,以你傳入的InvocationHandler作為參數(shù)創(chuàng)建一個代理類的實例并返回。
Proxy 類還有一些靜態(tài)方法,比如:
InvocationHandler getInvocationHandler(Object proxy):獲得代理對象對應(yīng)的調(diào)用處理器對象。
Class getProxyClass(ClassLoader loader, Class[] interfaces):根據(jù)類加載器和實現(xiàn)的接口獲得代理類。
InvocationHandler 接口中有方法:
invoke(Object proxy, Method method, Object[] args)
這個函數(shù)是在代理對象調(diào)用任何一個方法時都會調(diào)用的,方法不同會導(dǎo)致第二個參數(shù)method不同,第一個參數(shù)是代理對象(表示哪個代理對象調(diào)用了method方法),第二個參數(shù)是 Method 對象(表示哪個方法被調(diào)用了),第三個參數(shù)是指定調(diào)用方法的參數(shù)。
動態(tài)代理模式的簡單實現(xiàn)
public class DynamicProxyDemo {
public static void main(String[] args) {
//1.創(chuàng)建目標(biāo)對象
RealSubject realSubject = new RealSubject();
//2.創(chuàng)建調(diào)用處理器對象
ProxyHandler handler = new ProxyHandler(realSubject);
//3.動態(tài)生成代理對象
Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), handler);
//4.通過代理對象調(diào)用方法
proxySubject.request();
}
}
/**
* 主題接口
*/
interface Subject{
void request();
}
/**
* 目標(biāo)對象類
*/
class RealSubject implements Subject{
public void request(){
System.out.println("====RealSubject Request====");
}
}
/**
* 代理類的調(diào)用處理器
*/
class ProxyHandler implements InvocationHandler{
private Subject subject;
public ProxyHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//定義預(yù)處理的工作,當(dāng)然你也可以根據(jù) method 的不同進行不同的預(yù)處理工作
System.out.println("====before====");
//調(diào)用RealSubject中的方法
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
可以看到,我們通過newProxyInstance就產(chǎn)生了一個Subject 的實例,即代理類的實例,然后就可以通過Subject .request(),就會調(diào)用InvocationHandler中的invoke()方法,傳入方法Method對象,以及調(diào)用方法的參數(shù),通過Method.invoke調(diào)用RealSubject中的方法的request()方法。同時可以在InvocationHandler中的invoke()方法加入其他執(zhí)行邏輯。
泛型和Class類
從JDK 1.5 后,Java中引入泛型機制,Class類也增加了泛型功能,從而允許使用泛型來限制Class類,例如:String.class的類型實際上是Class<String>。如果Class對應(yīng)的類暫時未知,則使用Class<?>(?是通配符)。通過反射中使用泛型,可以避免使用反射生成的對象需要強制類型轉(zhuǎn)換。
泛型的好處眾多,最主要的一點就是避免類型轉(zhuǎn)換,防止出現(xiàn)ClassCastException,即類型轉(zhuǎn)換異常。以下面程序為例:
public class ObjectFactory {
public static Object getInstance(String name){
try {
//創(chuàng)建指定類對應(yīng)的Class對象
Class cls = Class.forName(name);
//返回使用該Class對象創(chuàng)建的實例
return cls.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
上面程序是個工廠類,通過指定的字符串創(chuàng)建Class對象并創(chuàng)建一個類的實例對象返回。但是這個對象的類型是Object對象,取出實例后需要強制類型轉(zhuǎn)換。
如下例:
Date date = (Date) ObjectFactory.getInstance("java.util.Date");
String string = (String) ObjectFactory.getInstance("java.util.Date");
上面代碼在編譯時不會有任何問題,但是運行時將拋出ClassCastException異常,因為程序試圖將一個Date對象轉(zhuǎn)換成String對象。
但是泛型的出現(xiàn)后,就可以避免這種情況。
public class ObjectFactory {
public static <T> T getInstance(Class<T> cls) {
try {
// 返回使用該Class對象創(chuàng)建的實例
return cls.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
在上面程序的getInstance()方法中傳入一個Class<T>參數(shù),這是一個泛型化的Class對象,調(diào)用該Class對象的newInstance()方法將返回一個T對象。
String instance = ObjectFactory.getInstance(String.class);
通過傳入String.class便知道T代表String,所以返回的對象是String類型的,避免強制類型轉(zhuǎn)換。
當(dāng)然Class類引入泛型的好處不止這一點,在以后的實際應(yīng)用中會更加能體會到。
使用反射來獲取泛型信息
通過指定類對應(yīng)的 Class 對象,可以獲得該類里包含的所有 Field,不管該 Field 是使用 private 修飾,還是使用 public 修飾。獲得了 Field 對象后,就可以很容易地獲得該 Field 的數(shù)據(jù)類型,即使用如下代碼即可獲得指定 Field 的類型。
// 獲取 Field 對象 f 的類型
Class<?> a = f.getType();
但這種方式只對普通類型的 Field 有效。如果該 Field 的類型是有泛型限制的類型,如 Map<String, Integer> 類型,則不能準(zhǔn)確地得到該 Field 的泛型參數(shù)。
為了獲得指定 Field 的泛型類型,應(yīng)先使用如下方法來獲取指定 Field 的類型。
// 獲得 Field 實例的泛型類型
Type type = f.getGenericType();
然后將 Type 對象強制類型轉(zhuǎn)換為 ParameterizedType 對象,ParameterizedType 代表被參數(shù)化的類型,也就是增加了泛型限制的類型。ParameterizedType 類提供了如下兩個方法。
getRawType():返回沒有泛型信息的原始類型。
getActualTypeArguments():返回泛型參數(shù)的類型。
下面是一個獲取泛型類型的完整程序。
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()取出Field類型只對普通類型的Field有效
Class<?> a = f.getType();
// 下面將看到僅輸出java.util.Map
System.out.println("score的類型是:" + a);
// 獲得Field實例f的泛型類型
Type gType = f.getGenericType();
// 如果gType類型是ParameterizedType對象
if(gType instanceof ParameterizedType)
{
// 強制類型轉(zhuǎn)換
ParameterizedType pType = (ParameterizedType)gType;
// 獲取原始類型
Type rType = pType.getRawType();
System.out.println("原始類型是:" + rType);
// 取得泛型類型的泛型參數(shù)
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("泛型類型是:");
for (int i = 0; i < tArgs.length; i++)
{
System.out.println("第" + i + "個泛型類型是:" + tArgs[i]);
}
}
else
{
System.out.println("獲取泛型類型出錯!");
}
}
}
輸出結(jié)果:
score 的類型是: interface java.util.Map
原始類型是: interface java.util.Map
泛型類型是:
第 0 個泛型類型是: class java.lang.String
第 1 個泛型類型是:class java.lang.Integer
從上面的運行結(jié)果可以看出,直接使用 Field 的 getType() 方法只能獲取普通類型的 Field 的數(shù)據(jù)類型:對于增加了泛型參數(shù)的類型的 Field,應(yīng)該使用 getGenericType() 方法來取得其類型。
Type 也是 java.lang.reflect 包下的一個接口,該接口代表所有類型的公共高級接口,Class 是 Type 接口的實現(xiàn)類。Type 包括原始類型、參數(shù)化類型、數(shù)組類型、類型變量和基本類型等。