一.術語較多
泛型類型 (泛型) :List<E>
泛型方法 :static <E> List<E> asList(E[] a)
原始類型(原生態類型)(raw type): List<E> 對應的原始類型就是 List
參數化類型 :List<String>
形式類型參數 :List<E>的E
實際類型參數(類型參數的實例) :List<String> 的 String
type of :<>
通配符 :?
二.注意
規定:Father是son的父類。
1.形式類型參數指定爲Father,傳入子類,自動向上轉型。
List<Father> list = new ArrayList<>();
list.add(new Son());
形式參數指定爲Son,傳入父類,顯示強制向下轉型。
List<Son> list = new ArrayList<>();
list.add((Son) new Father());
2.泛型類型轉換爲原始類型,編譯器對實際類型參數無法檢測了。
List<Father> before = new ArrayList<>();
before.add(new Father());
List after = before;
after.add("hello world");
before添加的是對象,將before賦值給原始類型after後,
檢測不到實際類型參數Father的存在,就添加了字符串對象。
這是不安全的操作。
3.List<? extends Father>:只能get 不能add
原因:
不知道具體類型參數是什麼,也就不知道具體泛型類的內部元素是什麼,所以add的值不能保證向下轉型成功。
但是上限是Father類型,返回值用Father來接受一定沒問題。
4.List<? super Father> : 只能add 不能get
原因:
不知道具體類型參數是什麼,但是指定了最低限度是Father類型,也就是實際List內部存儲的是Father以及Father超類類型。add的時候有一個要求:只能設置Father 以及 Father的子類。因爲一定能保證向上轉型。
但是返回值是不能確定的,如果實際返回的是Father的父類對象,拿Father來接收,會拋出異常。
三.疑惑
看Arraylist源碼看到一處代碼:
E elementData(int index) {
return (E) elementData[index];
}
上面類型擦除之後不就變成:
Object elementData(int index) {
return (Object) elementData[index];
}
那強轉有什麼意義呢?而且,在下面我們實際使用的時候,是怎麼直接可以賦值給String變量的呢?
實際使用:
ArrayList<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);
解答:
1.編寫(E)的意義:原來這是編譯期時編譯器的類型檢查,在代碼編譯時,編譯器認爲E是真實存在的類型,就好比我們使用時傳入的String類型,所以我們需要進行強轉,但是當按下運行編譯的那一刻,這個E就會被擦出了。
2.如何直接賦值的呢?原來編譯器會對代碼編譯爲字節碼的時候做手腳,會加上檢查類型轉換的字節碼指令。
附加:
就拿上面實際使用的例子生成的字節碼:
可以看到紅色框框的checkcast指令,引用了常量表中的String類型。實際對象的類型信息是可以通過對象頭中的class指針獲取。