花了好幾個小時看十頁的泛型簡介……人已經麻了,理解后趕緊把要點簡單記下來,備忘用。由于筆者僅是初學,如有錯誤歡迎評論區(qū)留言。
注:以下的例子完全可以舉一反三,舉出例子并用相同或類似的原因解釋為什么可以運行或者不可以運行。
零、泛型擦除
泛型擦除的原理:編譯時將類/方法中使用的泛型擦除,替換為原始類型(rawType)。一般地,普通無繼承的泛型默認繼承自Object,故編譯時一般替換為Object。
至于聲明時傳入的類型,編譯器將自動識別并阻止不匹配的元素傳入泛型類中。說到底,泛型僅僅是在編譯層面解析,底層仍舊是原始類型進行接收。而返回時,底層的原始類型對之前傳入的實際類型參數(shù)(<xx>)進行強制轉換,實現(xiàn)取出。
舉個例子:某小區(qū)有兩個垃圾桶,原本兩個桶無差別的,干濕垃圾可以隨便放后來進行垃圾分類了,居委會主任規(guī)定:1號桶放干垃圾,2號桶放濕垃圾。但總有人會記錯,把濕垃圾放入1號桶,導致垃圾工人收垃圾時被濺了一身后來主任搞了一臺濕度檢測儀,丟進去之前檢測一下垃圾濕度,不符合的就不讓放進去本質上1、2號垃圾桶的內部結構沒有任何變化,我們只是被濕度檢測儀攔截了。
作者:bravo1988
鏈接:https://zhuanlan.zhihu.com/p/255264414
Java的泛型擦除也決定了其泛型僅僅是假泛型。其泛型實現(xiàn)方式只是編譯器的語法糖罷了。
一、泛型的轉換問題
看下面兩行代碼:
List<String> a = new ArrayList<Integer>(); //編譯報錯
List<Integer> b = new ArratList<String>(); //編譯報錯
這兩行代碼是編譯器禁止的引用傳遞。泛型的初衷就是解決類型轉換的問題,在編譯器層面直接禁止了這種寫法,不必深究為什么不行。
再看兩行代碼:
List<Integer> a = new ArrayList(); //編譯警告
List b = new ArrayList<Integer>(); //編譯警告
原因引用下文:
第一種情況,可以實現(xiàn)與完全使用泛型參數(shù)一樣的效果,第二種則沒有效果。因為類型檢查就是編譯時完成的,new ArrayList()只是在內存中開辟了一個存儲空間,可以存儲任何類型對象,而真正設計類型檢查的是它的引用,因為我們是使用它引用list1來調用它的方法,比如說調用add方法,所以list1引用能完成泛型類型的檢查。而引用list2沒有使用泛型,所以不行。
鏈接:Java 基礎 - 泛型機制詳解 | Java 全棧知識體系 (pdai.tech)
不過實際測試中第二種也能用嘛。
二、泛型通配符<?>
這個東西就有意思多了。若在聲明引用中使用<?>,則不能使用:在方法內部泛型類的參數(shù)和返回值為泛型的方法。舉個例子:
public class App{
public static void main(String args[]){
Position<String> position = new Position<String>();
position.setX("11");
position.setY("22");
System.out.println(position.getX());
}
public void fun(Position<?> tmp){
String str = tmp.getX(); //Type mismatch: cannot convert from capture#1-of ? to String
}
}
class Position<T>{
private T x;
private T y;
public void setX(T x){
this.x = x;
}
public void setY(T y){
this.y = y;
}
public T getX(){
return this.x;
}
public T getY(){
return this.y;
}
}
這個比較容易理解了:創(chuàng)建tmp對象時傳入的是實際類型參數(shù)是通配符<?>,對應的Position類中T均為?,很明顯方法不能使用。
但是!如果tmp調用方法時傳入?yún)?shù)填入null,也是可以使用的。不過為了防止混淆,強烈不建議使用這種寫法。