Java 反射機制詳解(四)
Java 反射機制詳解(四)
4. 反射與泛型
定義一個泛型類:
public class DAO<T> {
//根據id獲取一個物件
T get(Integer id){
return null;
}
//儲存一個物件
void save(T entity){
}
}
再定義一個子類,繼承這個泛型類:
public class PersonDAO extends DAO<Person> {
}
父類中的泛型T,就相當於一個引數,當子類繼承這個類時,就要給這個引數賦值,這裡是把Person型別傳給了父類,還有一種做法 :
public class PersonDAO<T> extends DAO<T> {
}
進行測試:
@Test public void testAnnotation() throws Exception{ PersonDAO personDAO = new PersonDAO(); Person entity = new Person(); //呼叫父類的save方法,同時也把Person這個“實參”傳給了父類的T personDAO.save(entity); //這句的本意是要返回一個Person型別的物件 Person result = personDAO.get(1); System.out.print(result); }
問題出來了。這裡的get方法是父類的get方法,對於父類而言,方法返回值是一個T型別,當T的值為Person時,本該返回一個Person型別,但是必須用反射來建立這個物件(泛型方法返回一個物件),方法無非就是clazz.newInstance(),所以關鍵點就是根據T得到其對於的Class物件。那麼首先,在父類中定義一個欄位,表示T所對應的Class,然後想辦法得到這個clazz的值
public class DAO<T> { private Class<T> clazz; T get(Integer id){ return null; } }
如何獲得這個clazz呢?
@Test
public void test() throws Exception{
PersonDAO personDAO = new PersonDAO();
Person result = personDAO.get(1);
System.out.print(result);
}
public DAO(){
//1.
System.out.println("DAO's Constrctor...");
System.out.println(this); //結果是:[email protected]
//this:父類構造方法中的this指的是子類物件,因為此時是PersonDAO物件在呼叫
System.out.println(this.getClass()); //結果是:class com.atguigu.java.fanshe.PersonDAO
//2.
//獲取DAO子類的父類
Class class1 = this.getClass().getSuperclass();
System.out.println(class1); //結果是:class com.atguigu.java.fanshe.DAO
//此時只能獲的父類的型別名稱,卻不可以獲得父類的泛型引數
//3.
//獲取DAO子類帶泛型引數的子類
Type type=this.getClass().getGenericSuperclass();
System.out.println(type); //結果是:com.atguigu.java.fanshe.DAO<com.atguigu.java.fanshe.Person>
//此時獲得了泛型引數,然後就是把它提取出來
//4.
//獲取具體的泛型引數 DAO<T>
//注意Type是一個空的介面,這裡使用它的子類ParameterizedType,表示帶引數的類型別(即泛型)
if(type instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType) type;
Type [] arges = parameterizedType.getActualTypeArguments();
System.out.println(Arrays.asList(arges)); //結果是:[class com.atguigu.java.fanshe.Person]
//得到的是一個數組,因為可能父類是多個泛型引數public class DAO<T,PK>{}
if(arges != null && arges.length >0){
Type arg = arges[0];
System.out.println(arg); //結果是:class com.atguigu.java.fanshe.Person
//獲得第一個引數
if(arg instanceof Class){
clazz = (Class<T>) arg;
//把值賦給clazz欄位
}
}
}
}
所以就定義一個方法,獲得 Class 定義中宣告的父類的泛型引數型別
public class ReflectionTest {
/**
* 通過反射, 獲得定義 Class 時宣告的父類的泛型引數的型別
* 如: public EmployeeDao extends BaseDao<Employee, String>
* @param clazz: 子類對應的 Class 物件
* @param index: 子類繼承父類時傳入的泛型的索引. 從 0 開始
* @return
*/
@SuppressWarnings("unchecked")
public Class getSuperClassGenricType(Class clazz, int index){
Type type = clazz.getGenericSuperclass();
if(!(type instanceof ParameterizedType)){
return null;
}
ParameterizedType parameterizedType =
(ParameterizedType) type;
Type [] args = parameterizedType.getActualTypeArguments();
if(args == null){
return null;
}
if(index < 0 || index > args.length - 1){
return null;
}
Type arg = args[index];
if(arg instanceof Class){
return (Class) arg;
}
return null;
}
@SuppressWarnings("unchecked")
public Class getSuperGenericType(Class clazz){
return getSuperClassGenricType(clazz, 0);
}
@Test
public void testGetSuperClassGenricType(){
Class clazz = PersonDAO.class;
//PersonDAO.class
Class argClazz = getSuperClassGenricType(clazz, 0);
System.out.println(argClazz);
//結果是class com.atguigu.java.fanshe.Person
}
}
反射小結
1. Class: 是一個類; 一個描述類的類.
封裝了描述方法的 Method,描述欄位的 Filed,描述構造器的 Constructor 等屬性.
2. 如何得到 Class 物件:
2.1 Person.class
2.2 person.getClass()
2.3 Class.forName("com.atguigu.javase.Person")
3. 關於 Method:
3.1 如何獲取 Method:
a. getDeclaredMethods: 得到 Method 的陣列.
b. getDeclaredMethod(String methondName, Class ... parameterTypes)
3.2 如何呼叫 Method
a. 如果方法時 private 修飾的, 需要先呼叫 Method 的 setAccessible(true), 使其變為可訪問
b. method.invoke(obj, Object ... args);
4. 關於 Field:
4.1 如何獲取 Field: getField(String fieldName)
4.2 如何獲取 Field 的值:
a. setAccessible(true)
b. field.get(Object obj)
4.3 如何設定 Field 的值:
field.set(Obejct obj, Object val)
5. 瞭解 Constructor 和 Annotation
6. 反射和泛型.
6.1 getGenericSuperClass: 獲取帶泛型引數的父類, 返回值為: BaseDao<Employee, String>
6.2 Type 的子介面: ParameterizedType
6.3 可以呼叫 ParameterizedType 的 Type[] getActualTypeArguments() 獲取泛型引數的陣列.
附:反射的 Utils 函式集合
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* 反射的 Utils 函式集合
* 提供訪問私有變數, 獲取泛型型別 Class, 提取集合中元素屬性等 Utils 函式
* @author Administrator
*
*/
public class ReflectionUtils {
/**
* 通過反射, 獲得定義 Class 時宣告的父類的泛型引數的型別
* 如: public EmployeeDao extends BaseDao<Employee, String>
* @param clazz
* @param index
* @return
*/
@SuppressWarnings("unchecked")
public static Class getSuperClassGenricType(Class clazz, int index){
Type genType = clazz.getGenericSuperclass();
if(!(genType instanceof ParameterizedType)){
return Object.class;
}
Type [] params = ((ParameterizedType)genType).getActualTypeArguments();
if(index >= params.length || index < 0){
return Object.class;
}
if(!(params[index] instanceof Class)){
return Object.class;
}
return (Class) params[index];
}
/**
* 通過反射, 獲得 Class 定義中宣告的父類的泛型引數型別
* 如: public EmployeeDao extends BaseDao<Employee, String>
* @param <T>
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public static<T> Class<T> getSuperGenericType(Class clazz){
return getSuperClassGenricType(clazz, 0);
}
/**
* 迴圈向上轉型, 獲取物件的 DeclaredMethod
* @param object
* @param methodName
* @param parameterTypes
* @return
*/
public static Method getDeclaredMethod(Object object, String methodName, Class<?>[] parameterTypes){
for(Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){
try {
//superClass.getMethod(methodName, parameterTypes);
return superClass.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
//Method 不在當前類定義, 繼續向上轉型
}
//..
}
return null;
}
/**
* 使 filed 變為可訪問
* @param field
*/
public static void makeAccessible(Field field){
if(!Modifier.isPublic(field.getModifiers())){
field.setAccessible(true);
}
}
/**
* 迴圈向上轉型, 獲取物件的 DeclaredField
* @param object
* @param filedName
* @return
*/
public static Field getDeclaredField(Object object, String filedName){
for(Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){
try {
return superClass.getDeclaredField(filedName);
} catch (NoSuchFieldException e) {
//Field 不在當前類定義, 繼續向上轉型
}
}
return null;
}
/**
* 直接呼叫物件方法, 而忽略修飾符(private, protected)
* @param object
* @param methodName
* @param parameterTypes
* @param parameters
* @return
* @throws InvocationTargetException
* @throws IllegalArgumentException
*/
public static Object invokeMethod(Object object, String methodName, Class<?> [] parameterTypes,
Object [] parameters) throws InvocationTargetException{
Method method = getDeclaredMethod(object, methodName, parameterTypes);
if(method == null){
throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object + "]");
}
method.setAccessible(true);
try {
return method.invoke(object, parameters);
} catch(IllegalAccessException e) {
System.out.println("不可能丟擲的異常");
}
return null;
}
/**
* 直接設定物件屬性值, 忽略 private/protected 修飾符, 也不經過 setter
* @param object
* @param fieldName
* @param value
*/
public static void setFieldValue(Object object, String fieldName, Object value){
Field field = getDeclaredField(object, fieldName);
if (field == null)
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
makeAccessible(field);
try {
field.set(object, value);
} catch (IllegalAccessException e) {
System.out.println("不可能丟擲的異常");
}
}
/**
* 直接讀取物件的屬性值, 忽略 private/protected 修飾符, 也不經過 getter
* @param object
* @param fieldName
* @return
*/
public static Object getFieldValue(Object object, String fieldName){
Field field = getDeclaredField(object, fieldName);
if (field == null)
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
makeAccessible(field);
Object result = null;
try {
result = field.get(object);
} catch (IllegalAccessException e) {
System.out.println("不可能丟擲的異常");
}
return result;
}
}
原文連結:https://www.cnblogs.com/tech-bird/p/3525336.html