一個朋友圈泛型問題引發的“案子”

glmapper發表於2017-11-26

昨天朋友圈問了一個問題:
對於下面的list,何如在list新增一個Integer型整數?

ArrayList<String> list = new ArrayList<String>();複製程式碼

有這樣幾種回答:

  • 1.不知道(非專業回答)
  • 2.硬塞(非專業回答)
  • 3.把String 改成Integer再新增(違背了問題初衷)
  • 4.把String改成Object,可以加任意型別(違背了問題初衷)
  • 5.String換成萬用字元
  • 6.反射

對於1、2就不說了,屬於搞事情的!3、4、5三種方式違背了問題的初衷,如果可以改,那我們直接new三個ArrayList就可以了。6反射,這個是無限接近的,那麼這個和反射有什麼關係呢?下來看下下面幾個例子:

public static void main(String[] args) {

        ArrayList list=new ArrayList();

        ArrayList<String> str_list=new ArrayList<String>();

        ArrayList<Integer> int_list=new ArrayList<Integer>();

        ArrayList<Object> obj_list=new ArrayList<Object>();
        //物件比較
        System.out.println(list == str_list);
        System.out.println(list == int_list);
        System.out.println(list == obj_list);

        //物件的執行時class比較
        System.out.println(list.getClass() == str_list.getClass());
        System.out.println(list.getClass() == int_list.getClass());
        System.out.println(list.getClass() == obj_list.getClass());
    }複製程式碼

結果:

false
false
false
true
true
true複製程式碼

其實上面三個很容易理解,不同物件在記憶體中的地址肯定是不同的,因此均為false;下面三個均為true?是的,確實為true,這就引出了朋友圈的那個問題。為什麼不同的三個物件,他們的getClass是一樣的,不應該是有三個不同的hashCode嗎?這個其實就是泛型編譯時和執行時的問題
對於泛型來說,泛型只在編譯階段有效,編譯之後,集合的泛型是去泛型化的;原因:由於JVM泛型的擦除機制,在執行時JVM是不知道泛型資訊的
因此:java集合中的泛型,是來約束使用者的錯誤輸入的,只在編譯時有效;
在回到問題最初,我們怎麼才能將一個Integer對像放入上面定義的list中呢?既然集合中的泛型是編譯時有效的,那我我們就可以通過繞過編譯的方式進行插入。那麼如何繞過編譯時的校驗呢?答案就是用反射;我們知道JAVA反射機制是指:
“在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為java語言的反射機制。
OK,再來看程式:

        ArrayList<String> str_list=new ArrayList<String>();
        //獲取類資訊
        Class c=str_list.getClass();
        //獲取add方法
        Method m=c.getMethod("add", Object.class);
        //執行時呼叫add方法
        m.invoke(str_list, 20);
        //輸出當前str_list
        System.out.println(str_list);複製程式碼

結果:

[20]複製程式碼

從結果可以看出,我們完成了在list中新增Integer的任務。
【泛型、反射、編譯時、執行時】
大家週末愉快!

相關文章