1. 程式人生 > >Java 反射機制詳解(四)

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