原文: https://lsy.iteye.com/blog/220264
反射是Java語言中很重要的一個(gè)組成部分,所以就此話題討論的資源可謂數(shù)之不盡,日常開發(fā)也會(huì)經(jīng)常使用到關(guān)于反射的 Reflection API 。
Java5.0 Tiger 出現(xiàn)以后,更對(duì)反射API有了新的擴(kuò)展,盡管討論的話題很多,不過我還是覺得不夠全面,尤其是對(duì)泛型這一塊,所以就我所知,再花力氣總結(jié)一番
首先反射的入口是從 Class 開始的,所以如何獲取 Class 就變得十分關(guān)鍵了。這里總結(jié)了幾種方式:
- 通過
${name}.class語法。這里${name}可以是對(duì)象,也可以是原始數(shù)據(jù)類型,不過別忘了void.class和Void.class - 通過
${name}.TYPE語法。這里${name}是八種原始數(shù)據(jù)的包裝類和Void.TYPE - 通過對(duì)象的
getClass()方法。 - 通過 Class 對(duì)象的
forName()方法 - 通過類 Class 的
getSuperclass()獲取父親類Class - 通過類 Class 的
getEnclosingClass()獲取外部類Class - 通過類 Class 的
getClasses()和getDeclaredClasses()獲取內(nèi)部類Class
下面是一張表用來說明 getClasses() 和 getDeclaredClasses() 兩個(gè)方法,稍后還會(huì)用該表說明其他 Reflection API
| Member | Class API | Return type | Inherited members | Private members |
|---|---|---|---|---|
| Class | getDeclaredClasses() | Array | N | Y |
| getClasses() | Array | Y | N | |
| Field | getDeclaredField() | Single | N | Y |
| getField() | Single | Y | N | |
| getDeclaredFields() | Array | N | Y | |
| getFields() | Array | Y | N | |
| Method | getDeclaredMethod() | Single | N | Y |
| getMethod() | Single | Y | N | |
| getDeclaredMethods() | Array | N | Y | |
| getMethods() | Array | Y | N | |
| Constructor | getDeclaredConstructor() | Single | N/A | Y |
| getConstructor() | Single | N/A | N | |
| getDeclaredConstructors() | Array | N/A | Y | |
| getConstructors() | Array | N/A | N |
如上表所示, getClasses() 擁有繼承的特點(diǎn),可以獲取父親級(jí)定義的內(nèi)部類,而不能訪問定義為private的內(nèi)部類;
而 getDeclaredClasses() 剛好相反,可以訪問定義為 private 的內(nèi)部類,卻無法獲取父親級(jí)定義的內(nèi)部類
成功獲取了Class以后,那么就可以開始訪問 Field, Method 和 Constructor 了,他們都繼承自 java.lang.reflect.Member 。
從上表已經(jīng)很容易可以看出各個(gè)成員是否擁有繼承特性,是否能夠訪問私有成員,返回類型的數(shù)量這些信息。
這里需要注意一點(diǎn),由于 Constructor 是無法被繼承的,所以 Constructor 成員任何方法都沒有繼承的特性。
另外 Field 和 Method 在賦值或者調(diào)用的之前需要留意是否在操作私有成員,如果是那么需要先修改可訪問度,執(zhí)行 setAccessible(true) 。
還有一點(diǎn)就是 Method 成員的 getDeclaredMethod() 和 getMethod() 方法都需要兩個(gè)參數(shù),一個(gè)是方法的名稱,另外一個(gè)參數(shù) Class 的數(shù)組,這里需要感謝 Java5.0 引入不定長(zhǎng)參數(shù)的特點(diǎn),使我們可以在某些情況下少傳入一個(gè)參數(shù),如:
假設(shè)Order類有下列方法
public Long getId() { return id; }
5.0以前獲取該方法的代碼如下
Method getId = Order.class.getMethod("getId", new Class[0]);
而5.0僅需要寫
Method getId = Order.class.getMethod("getId");
現(xiàn)在說說5.0泛型出現(xiàn)之后, Java Reflection API 的新特點(diǎn)。
首先增加一個(gè)接口 java.lang.reflect.Type ,其下一共有4個(gè)接口繼承了它, TypeVariable, ParameterizedType, GenericArrayType 和 WildcardType,下面逐個(gè)分析。
1.TypeVariable
我們知道泛型信息會(huì)在編譯時(shí)被JVM編譯時(shí)轉(zhuǎn)換為定義的一個(gè)特定的類型,這減少了應(yīng)用程序逐步向下檢查類型的開支,避免了發(fā)生 ClassCastException 的危險(xiǎn)。
而 TypeVariable 就是用來反映在JVM編譯該泛型前的信息。舉個(gè)例子,假設(shè) BaseOrder 類定義有如下一個(gè)方法
public class BaseOrder<M extends Object & Serializable, N extends Comparable<N>> implements IBaseOrder {
public M getManufactory(){
return manufactory;
}
}
這時(shí)候我們可以通過如下代碼獲取該泛型 Type , 并且經(jīng)過測(cè)試該 Type 就是 TypeVariable
Field manufactoryField = BaseOrder.class.getDeclaredField("manufactory");
type = manufactoryField.getGenericType();
assertTrue("The type of field manufactory is an instance of TypeVariable", type instanceof TypeVariable);
TypeVariable tType = (TypeVariable)type;
assertEquals("The name of this TypeVariable is M", "M", tType.getName());
assertEquals("The TypeVariable bounds two type", 2, tType.getBounds().length);
assertEquals("One type of these bounds is Object", Object.class, tType.getBounds()[0]);
assertEquals("And annother si Serializable", Serializable.class, tType.getBounds()[1]);
通過 getName() 方法可以獲取該泛型定義的名稱,而更為重要的是 getBounds() 方法,可以判斷該泛型的邊界。
2.ParameterizedType
這個(gè)接口就比較出名了,在過去討論最多的問題就是 GenericDao<T> 中,如何獲取 T.class 的問題了。這里再翻出來過一遍,加入上述的類B aseOrder 定義不變,新定義一個(gè)Order對(duì)象,代碼如下:
public class Order extends BaseOrder<Customer, Long> implements IOrder, Serializable {
}
那么如何通過 Order 獲取到 Customer 呢?
Type genericSuperclass = Order.class.getGenericSuperclass();
assertTrue("Order's supper class is a type of ParameterizedType.", genericSuperclass instanceof ParameterizedType);
ParameterizedType pType = (ParameterizedType)genericSuperclass;
assertEquals("Order's supper class is BaseOrder.", BaseOrder.class, pType.getRawType());
Type[] arguments = pType.getActualTypeArguments();
assertEquals("getActualTypeArguments() method return 2 arguments.", 2, arguments.length);
for (Type type : arguments) {
Class clazz = (Class)type;
if(!(clazz.equals(Customer.class)) && !(clazz.equals(Long.class))){
assertTrue(false);
}
}
可以看出通過Order類的 getGenericSuperclass() 方法將返回一個(gè)泛型,并且它就是 ParameterizedType 。這個(gè)接口的 getRawType() 方法和 getActualTypeArguments() 都非常重要.
getRawType() 方法返回的是承載該泛型信息的對(duì)象,而 getActualTypeArguments() 將會(huì)返回一個(gè)實(shí)際泛型對(duì)象的數(shù)組。
這里先提及一下Class對(duì)象中 getGenericSuperclass() 和 getSuperclass() 兩個(gè)方法的區(qū)別,后文還有詳細(xì)說明。
getGenericSuperclass() 方法首先會(huì)判斷是否有泛型信息,有那么返回泛型的 Type ,沒有則返回 Class ,方法的返回類型都是 Type ,這是因?yàn)?Tiger 中 Class 也實(shí)現(xiàn)了 Type 接口。將父親按照 Type 接口的形式返回,而 getSuperclass() 直接返回父親的 Class 。
3.GenericArrayType
這個(gè)接口比較好理解。如果泛型參數(shù)是一個(gè)泛型的數(shù)組,那么泛型 Type 就是 GenericArrayType ,它的 getGenericComponentType() 將返回被JVM編譯后實(shí)際的數(shù)組對(duì)象。這里假設(shè)上文中BaseOrder有一個(gè)方法如下:
public String[] getPayments(String[] payments, List<Product> products){
return payments;
}
可以看出該方法的參數(shù)中有泛型信息,測(cè)試一下:
Method getPayments = BaseOrder.class.getMethod("getPayments", new Class[]{String[].class, List.class});
types = getPayments.getGenericParameterTypes();
assertTrue("The first parameter of this method is GenericArrayType.", types[0] instanceof GenericArrayType);
GenericArrayType gType = (GenericArrayType)types[0];
assertEquals("The GenericArrayType's component is String.", String.class, gType.getGenericComponentType());
發(fā)現(xiàn)這個(gè) getPayments() 方法中的一個(gè)參數(shù) String[] payments 是一個(gè) GenericArrayType ,通過 getGenericComponentType() 方法返回的是 String.class 。這是怎么回事呢?
這里我們回過頭去看 Class 對(duì)象的 getGenericSuperclass() 方法和 getSuperclass() 方法,如果把它們說成是一對(duì)的話,那么這里的 getGenericParameterTypes() 和 getParameterTypes() 就是另外一對(duì)。
也就是說 getGenericParameterTypes() 首先判斷該方法的參數(shù)中是否有泛型信息,有那么返回泛型Type的數(shù)組,沒有那么直接按照Class的數(shù)組返回;
而getParameterTypes()就直接按照Class的數(shù)組返回。非常相似吧,其原因就是這些成對(duì)的方法都有一個(gè)共同點(diǎn)就是判斷是否有泛型信息,可以查看 Tiger 的源代碼:
public Type[] getGenericParameterTypes() {
if (getGenericSignature() != null)
return getGenericInfo().getParameterTypes();
else
return getParameterTypes();
}
而這類成對(duì)出現(xiàn)的方法還很多,如Method對(duì)象定義的 getGenericReturnType() 和 getReturnType() , getGenericExceptionTypes() 和 getExceptionTypes() ,
Field對(duì)象定義的 getGenericType() 和 getType() 。
4.WildcardType
這個(gè)接口就是獲取通配符泛型的信息了。這里假設(shè)上述的BaseOrder定義有一個(gè)屬性
private Comparable<? extends Customer> comparator;
現(xiàn)在就來獲取泛型?的信息,測(cè)試代碼如下:
Field comparatorField = BaseOrder.class.getDeclaredField("comparator");
ParameterizedType pType = (ParameterizedType)comparatorField.getGenericType();
type = pType.getActualTypeArguments()[0];
assertTrue("The type of field comparator is an instance of ParameterizedType, and the actual argument is an instance of WildcardType.", type instanceof WildcardType);
WildcardType wType = (WildcardType)type;
assertEquals("The upper bound of this WildcardType is Customer.", Customer.class, wType.getUpperBounds()[0]);
首先我們獲取到 comparator 這個(gè)屬性,通過它的 getGenericType() 方法我們拿到了一個(gè)Type,可以看出它是一個(gè) ParameterizedType ,而 ParameterizedType 的 actual argument 就是我們需要的 WildcardType ,
這個(gè)接口有兩個(gè)主要的方法, getLowerBounds() 獲取該通配符泛型的下界信息,相反 getUpperBounds() 方法獲取該通配符泛型的上界信息。
說完了這四個(gè)接口,我們?cè)倩剡^頭來看看Method對(duì)象,它還定義有一個(gè)方法 getTypeParameters() ,這又是干什么的呢?我們知道泛型是不能出現(xiàn)在靜態(tài)的成員,靜態(tài)的方法,或者靜態(tài)的初始化器的邏輯中的。如下列代碼都是錯(cuò)誤的:
//error
private static T customer;
//error
public static T getCustomer(){
return customer;
}
//error
static {
customerHolder = new HashSet<T>();
}
不過靜態(tài)的方法的參數(shù)卻是可以被泛化的,如:
public static <B extends BusinessType, S extends Serializable> Customer getSpcialCustomer(List<B> types, S serial){
return new Customer();
}
這個(gè)方法我們就可以通過getTypeParameters()方法來獲取它的參數(shù)化信息,代碼如下:
Method getSpcialCustomer = SalePolicy.class.getMethod("getSpcialCustomer", new Class[]{List.class,Serializable.class});
types = getSpcialCustomer.getTypeParameters();
assertEquals("The method declared two TypeVariable.", 2, types.length);
assertEquals("One of the TypeVariable is B.", "B", ((TypeVariable)types[0]).getName());
assertEquals("And another is S.", "S", ((TypeVariable)types[1]).getName());
最后,說一下 Annotation ,成員可以通過 isAnnotationPresent(annotationClass) 和 getAnnotation(annotationClass) 方法來判斷是否被某個(gè) Annotation 標(biāo)注,
不過需要注意的時(shí)該annotation自身必須被標(biāo)注為 @Retention(RetentionPolicy.RUNTIME)。