標籤:
當我們聲明了一個泛型的介面或類,或需要一個子類繼承至這個泛型類,而我們又希望利用反射擷取這些泛型參數資訊。這就是本文將要介紹的ReflectionUtil就是為瞭解決這類問題的協助工具輔助類,為java.lang.reflect標準庫的工具類。它提供了便捷的訪問泛型物件類型(java.reflect.Type)的反射方法。
本文假設你已經瞭解java反射知識,並能熟練的應用。如果還不瞭解java反射知識,那麼你可以先移步到Oracel反射課程,這可能是你開始學習反射的好起點.
ReflectionUtil中包含以下幾種功能:
- 通過Type擷取對象class;
- 通過Type建立對象;
- 擷取泛型對象的泛型化參數;
- 檢查對象是否存在預設建構函式;
- 擷取指定類型中的特定field類型;
- 擷取指定類型中的特定method傳回型別;
- 根據字串標示擷取枚舉常量;
- ReflectionUtil.
通過Type擷取對象class
private static final String TYPE_NAME_PREFIX = "class ";public static String getClassName(Type type) { if (type==null) { return ""; } String className = type.toString(); if (className.startsWith(TYPE_NAME_PREFIX)) { className = className.substring(TYPE_NAME_PREFIX.length()); } return className;}public static Class<?> getClass(Type type) throws ClassNotFoundException { String className = getClassName(type); if (className==null || className.isEmpty()) { return null; } return Class.forName(className);}
方法ReflectionUtil#getClass(Type)實現了從java.lang.reflect.Type擷取java.lang.Class對象名稱。這裡利用了Type的toString方法擷取所在類型的class。如“class some.package.Foo”,截取後部分class名稱,在利用Class.forName(String)擷取class對象。
通過Type建立對象
public static Object newInstance(Type type) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class<?> clazz = getClass(type); if (clazz==null) { return null; } return clazz.newInstance();}
方法ReflectionUtil#newInstance(Type type)實現根據Type構造對象執行個體。在這裡輸入的Type不能是抽象類別、介面、數群組類型、以及基礎類型、Void否則會發生InstantiationException異常。
擷取泛型對象的泛型化參數
首先假設我們有如下兩個對象:
public abstract class Foo<T> { //content}public class FooChild extends Foo<Bar> { //content}
怎麼擷取子類在Foo中傳入的泛型Class類型呢?
比較常用的做法有以下兩種:
強制FooChild傳入自己的class類型(這也是比較常用的做法):
public abstract class Foo<T> { private Class<T> tClass; public Foo(Class<T> tClass) { this.tClass = tClass; } //content}public class FooChild extends Foo<Bar> { public FooChild() { super(FooChild.class); } //content}
利用反射擷取:
public static Type[] getParameterizedTypes(Object object) { Type superclassType = object.getClass().getGenericSuperclass(); if (!ParameterizedType.class.isAssignableFrom(superclassType.getClass())) { return null; } return ((ParameterizedType)superclassType).getActualTypeArguments();}
方法ReflectionUtil#getParameterizedTypes(Object)利用反射擷取運行時泛型參數的類型,並數組的方式返回。本例中為返回一個T類型的Type數組。
為了Foo得到T的類型我們將會如下使用此方法:
...Type[] parameterizedTypes = ReflectionUtil.getParameterizedTypes(this);Class<T> clazz = (Class<T>)ReflectionUtil.getClass(parameterizedTypes[0]);...
注意:
在java.lang.reflect.ParameterizedType#getActualTypeArguments() documentation:的文檔中你能看見如下文字:
in some cases, the returned array can be empty. This can occur. if this type represents a non-parameterized type nested within a parameterized type.
當傳入的對象為非泛型型別,則會返回空數組形式。
檢查對象是否存在預設建構函式
public static boolean hasDefaultConstructor(Class<?> clazz) throws SecurityException { Class<?>[] empty = {}; try { clazz.getConstructor(empty); } catch (NoSuchMethodException e) { return false; } return true;}
方法ReflectionUtil#hasDefaultConstructor利用java.lang.reflect.Constructor檢查是否存在預設的無參建構函式。
擷取指定類型中的特定field類型
public static Class<?> getFieldClass(Class<?> clazz, String name) {
if (clazz==null || name==null || name.isEmpty()) { return null;}name = name.toLowerCase();Class<?> propertyClass = null;for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); if (field.getName().equals(name)) { propertyClass = field.getType(); break; }}return propertyClass;
}
在某些情況下你希望利用已知的類型資訊和特定的欄位名字想擷取欄位的類型,那麼ReflectionUtil#getFieldClass(Class<?>, String)可以協助你。ReflectionUtil#getFieldClass(Class<?>, String) 利用Class#getDeclaredFields()擷取欄位並迴圈比較java.lang.reflect.Field#getName()欄位名稱,返回欄位所對應的類型對象。
擷取指定類型中的特定method傳回型別
public static Class<?> getMethodReturnType(Class<?> clazz, String name) { if (clazz==null || name==null || name.isEmpty()) { return null; } name = name.toLowerCase(); Class<?> returnType = null; for (Method method : clazz.getDeclaredMethods()) { if (method.getName().equals(name)) { returnType = method.getReturnType(); break; } } return returnType;}
方法ReflectionUtil#getMethodReturnType(Class<?>, String)可以協助你根據物件類型和方法名稱擷取其所對應的方法傳回型別。ReflectionUtil#getMethodReturnType(Class<?>, String)利用Class#getDeclaredMethods()並以java.lang.reflect.Method#getName()比對方法名稱,返回找到的方法的傳回值類型(Method#getReturnType()).
根據字串標示擷取枚舉常量
@SuppressWarnings({ "unchecked", "rawtypes" })public static Object getEnumConstant(Class<?> clazz, String name) { if (clazz==null || name==null || name.isEmpty()) { return null; } return Enum.valueOf((Class<Enum>)clazz, name);}
方法ReflectionUtil#getEnumConstant(Class<?>, String)為利用制定的枚舉類型和枚舉名稱擷取其對象。這裡的名稱必須和存在的枚舉常量匹配。
ReflectionUtil
你可以從這裡下載ReflectionUtil.java. 原英文版地址: http://qussay.com/2013/09/28/handling-java-generic-types-with-reflection/
(翻譯)反射處理java泛型