Java反射—方法的反射、深入瞭解泛型

twilight0402發表於2017-06-28

版權宣告:本文為博主原創文章,轉載請註明出處。 https://blog.csdn.net/twilight_karl/article/details/73848996

方法的反射

invoke(物件,引數列表)
1、獲得類型別
Class data = a.getClass();

2、獲得方法
– getMethod(“方法名”,可變引數(Class[])) 獲得自身和繼承的public方法
– getDeclaredMethod(“方法名”,可變引數(Class[])) 獲得自身的所有方法,不包括繼承的方法

    Method print = data.getMethod("print", new Class[]{int.class,int.class});
    Method print2 = data.getDeclaredMethod("print", new Class[]{String.class,String.class});

3、執行方法
invoke(物件obj,引數列表) 使用物件obj呼叫方法

        print.invoke(a, 10, 20);
        print2.invoke(a, new String[]{"hello","world"});

如果沒有引數,可以不寫:

        Method print3 = data.getMethod("print");
        print3.invoke(a);

通過反射了解泛型的本質

所謂泛型,是在編譯階段判斷變數型別是否滿足泛型要求。例如ArrayList<Stirng> 在編譯時如果add一個int型別的變數肯定會出錯,但是,在編譯完成後,不同型別的泛型實際上是一樣的。如下:

        ArrayList list1 = new ArrayList();
        ArrayList<String> list2 = new ArrayList<String>();

        Class c1 = list1.getClass();
        Class c2 = list2.getClass();

        System.out.println(c1 == c2); // true

說明不同型別的泛型在編譯後是一樣的,泛型只是幫助判斷變數型別的一種機制。
String型別的泛型,只能加入String型別的變數,所以集合中儲存的變數原本都是String型別。而Object型別的泛型可以加入任意型別的變數,所有變數都會被轉換為Object型別後儲存,所以在去變數時,需要手動的將Object型別強制轉換為指定的型別。

反射機制是在編譯之後完成的。所以可以利用反繞過泛型的編譯:

        Method add = c2.getMethod("add", Object.class);
        add.invoke(list2, 100);

        System.out.println(list2.size());       // 1
        System.out.println(list2);              // [100]

上面的例子在String型別的泛型集合中加入了整形變數,說明泛型的型別檢查是在執行之前進行的。編譯過後不會有泛型的型別檢查,所以不會報錯。另外我在編寫的過程中還發現一個細節:

        Method add = c2.getMethod("add", String.class);

上面這行程式碼會報錯,c2是ArrayList<String>的類型別。一個String型別的泛型集合卻無法獲得String型別的add方法。原因是在編譯過後所有型別會被擦除。在ArrayList的原始碼中用一個Object型別的陣列來儲存資料。編譯過變數會被儲存在Object陣列中,所以add方法中的型別也會被轉化為Object

    transient Object[] elementData; // non-private to simplify nested class access

個人理解,歡迎指正

參考資料:反射


相關文章