昨天朋友圈問了一個問題:
對於下面的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的任務。
【泛型、反射、編譯時、執行時】
大家週末愉快!