天天看點

結合反射,擷取泛型對象的類成員變量的值

網上找到的都是反射擷取已知對象的,索性自己實作了。

先上代碼:

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by jackie on 2016/7/14.
 */
public class TestReflect<T> {
    public static void main(String args[]) throws NoSuchFieldException, IllegalAccessException, InstantiationException, ClassNotFoundException {
        Message m1 = new Message();
        Message m2 = new Message();
        List<Message> list = new ArrayList<>();
        list.add(m1);
        list.add(m2);
        這裡是我測試通過集合類工具加工的不可修改List是否會影響泛型傳遞後的對象,
        // 不過顯然不會,隻是對List的修改做了限制。unmodifiableList的使用場景請自行百度
        List<Message> unmodifiedList = Collections.unmodifiableList(list);
        //是以這裡如果業務沒涉及可以不管,下面方法的傳參unmodifiableList可以改成list。
        new TestReflect<Message>().printObjectValue(unmodifiedList, Message.class.getName());
    }

    public void printObjectValue(List<T> object, String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Object ot = Class.forName(className).newInstance();
        Class c = ot.getClass();
        Field[] fields = c.getDeclaredFields();//取得所有類成員變量

//        //周遊所有類成員變量
//        for (Field f:fields
//             ) {
//            System.out.println(f.getName());
//        }
//        //周遊所有方法
//        Method[] methods = c.getMethods();
//        for (Method m:methods
//             ) {
//            System.out.println(m.getName());
//        }

        //取消每個屬性的安全檢查
        for(Field f:fields){
            f.setAccessible(true);
        }

        //列印傳入的每個對象的所有類成員屬性值
        for (int j = 0; j < object.size(); j++)
            for (int i = 0; i < fields.length; i++) {
                try {
                    System.out.println(fields[i].getName() + ":" + fields[i].get(object.get(j)));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    }
}
           
/**
 * Created by jackie on 2016/7/15.
 */
public class Message {
    public Message(){
        num=1;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    private int num;

    private String message="你好";



    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
           

無論傳入的是什麼類型的對象,如果直接拿泛型類去反射都是拿到泛型本身的屬性和方法,拿不到類對象的屬性方法,于是經常會抛類似這樣的異常:java.lang.IllegalArgumentException: Can not set java.lang.reflect.Constructor field java.lang.Class.cachedConstructor to Message

我的解決思路很簡單:傳進來類對象的同時也把對象的ClassName傳進來,再用反射API構造該類對象的執行個體,再拿到該對象的Class再反射獲得該類對象的屬性(getDeclareFields和getFields的差別自己注意下)和方法,接着就可以按正常反射擷取對象屬性值的操作來實作了。

如果按以上操作能抛出類似上面說的異常,請在調用該方法前檢查要傳入的對象的類對象和傳入的類對象名稱是否對應。比如我在業務中遇到的持久層封裝,進行SQL查詢後是将取到的值放到一個Object[]數組的,天真的我隻是看到傳回的對象類型為QueryPage<Message>,再getContent取到List<Message>類型就以為裡面放的是Message的對象數組,坑了很久才發現裡面放的是Object[]數組,隻保留了值,丢失了鍵。為什麼會這樣呢,等我搞清楚再更帖。

發現原因了,hibernate的HQL語句如果寫成select num from Message;這樣的話查詢結果是放在Object[]裡,如果寫成select new Message(num) from Message;則查詢結果會放在一個Message對象中(當然要為該對象寫對應的構造方法,且這裡的new Message是要寫全包名)。