泛型的設計初衷:是為了減少類型轉換錯誤產生的安全隱患,而不是為了實現任意化。
泛型可以應用在類、接口和方法的創建中,分別稱為泛型類、泛型接口和泛型方法
- 泛型類在類名後面用尖括號表示泛型
<code>public class HelloWorld{ /<code>
private T t;
public T getValue() {
return t;
}
public void setValue(T t) {
this.t = t;
}
}
- 泛型方法在方法聲明中加入泛型
<code>//這是泛型方法
staticvoid printHelloWorld(T t){ /<code>
LOGGER.info(t);
}
//這是一個普通方法
public T getT() {
return t;
}
- 泛型接口跟類類似
<code>public interface Generator{ /<code>
public T next();
}
總結:
- 有
帶尖括號的 才能表示是泛型類或方法或接口。而只有T的只能表示是泛型類型 - 泛型類型的命名並不是必須為T,也可以使用其他字母,如X、K等,只要是命名為單個大寫字即可。
- 雖然沒有強制的命名規範,但是為了便於代碼閱讀,也形成了一些約定俗成的命名規範,如下:
T通用泛型類型,通常作為第一個泛型類型S
通用泛型類型,如果需要使用多個泛型類型,可以將S作為第二個泛型類型
U通用泛型類型,如果需要使用多個泛型類型,可以將U作為第三個泛型類型V通用泛型類型,如果需要使用多個泛型類型,可以將V作為第四個泛型類型E集合元素 泛型類型,主要用於定義集合泛型類型K映射-鍵 泛型類型,主要用於定義映射泛型類型V映射-值 泛型類型,主要用於定義映射泛型類型N數值 泛型類型,主要用於定義數值類型的泛型類型
通配符、上下邊界、無界
如果不對泛型類型做限制,則泛型類型可以實例化成任意類型,這種情況可能產生某些安全性隱患。
為了限制允許實例化的泛型類型,我們需要一種能夠限制泛型類型的手段,即:有界泛型類型
<code>//有界泛型類型語法 - 繼承自某父類/<code>
//有界泛型類型語法 - 實現某接口
//有界泛型類型語法 - 多重邊界
//示例//N標識一個泛型類型,其類型只能是Number抽象類的子類
//T標識一個泛型類型,其類型只能是Person類型的子類,並且實現了Comparable 和 Map接口
上邊界通配符( extends 父類型>)
- 上界:用 extends 關鍵字聲明,表示參數化的類型可能是所指定的類型,或者是此類型的子類。
- 類型參數列表中如果有多個類型參數上限,用逗號分開private
E test(K arg1, E arg2){} - 上邊界類型通配符可以確定父類型
- 在獲取數據時,由於固定了上界,類型肯定是返回類型的子類型,可以通過向上轉型成功獲取。
- 在寫入數據時,由於向下轉型存在很大風險,Java泛型為了減低類型轉換的安全隱患,不允許這種操作。
<code>Plate extends Fruit> p=new Plate<apple>(new Apple());
//不能存入任何元素
p.set(new Fruit()); //Error
p.set(new Apple()); //Error
//讀取出來的東西只能存放在Fruit或它的基類裡。
Fruit newFruit1=p.get();
Object newFruit2=p.get();
Apple newFruit3=p.get(); //Error/<apple>/<code>
原因是編譯器只知道容器內是Fruit或者它的派生類,但具體是什麼類型不知道。可能是Fruit?可能是Apple?也可能是Banana,RedApple,GreenApple?編譯器在看到後面用Plate賦值以後,盤子裡沒有被標上有“蘋果”。而是標上一個佔位符:CAP#1,來表示捕獲一個Fruit或Fruit的子類,具體是什麼類不知道,代號CAP#1。然後無論是想往裡插入Apple或者Meat或者Fruit編譯器都不知道能不能和這個CAP#1匹配,所以就都不允許。
下邊界通配符( super 子類型>)
- 下界通配符 super T>不影響往裡面存儲,但是讀取出來的數據只能是Object類型。
- 和上界相對的就是下界 ,語法表示為: super T>
<code>Plate super Fruit> p=new Plate<fruit>(new Fruit());
//存入元素正常
p.set(new Fruit());
p.set(new Apple());
//讀取出來的東西只能存放在Object類裡。
Apple newFruit3=p.get(); //Error
Fruit newFruit1=p.get(); //ErrorO
bject newFruit2=p.get();
/<fruit>/<code>
因為下界規定了元素的最小粒度的下限,實際上是放鬆了容器元素的類型控制。既然元素是Fruit的基類,那往裡存粒度比Fruit小的都可以。但往外讀取元素就費勁了,只有所有類的基類Object對象才能裝下。但這樣的話,元素的類型信息就全部丟失。
- 頻繁往外讀取內容的,適合用上界Extends。
- 經常往裡插入的,適合用下界Super。
無邊界通配符(>)
無邊界通配符> 等同於上邊界通配符 extends Object>,所以關於無邊界通配符(>)就很好理解了。
因為可以確定父類型是Object,所以可以以Object去獲取數據(向上轉型)。但是不能寫入數據。
與>的區別
?和 T 都表示不確定的類型,區別在於我們可以對 T 進行操作,但是對 ?不行,比如如下這種
<code>// 可以
T t = operate();
// 不可以
?car = operate();/<code>
T 是一個 確定的類型,通常用於泛型類和泛型方法的定義
?是一個 不確定的類型,通常用於泛型方法的調用代碼和形參,不能用於定義類和泛型方法。
類型參數 T 只具有 一種 類型限定方式:
<code>T extends A/<code>
但是通配符 ? 可以進行 兩種限定:
<code>? extends A? super A /<code>
T 可以多重限定 而 ? 不行
<code>/<code>
泛型使用的限制
泛型不能使用基本類型
<code>// 編譯前類型檢查報錯
Listlist = new List /<code>();
泛型不允許進行實例化
<code>void test(T t){ /<code>
//編譯前類型檢查報錯
t = new T();
}
泛型不允許進行靜態化
<code>static class Demo{ /<code>
// 編譯前類型檢查報錯
private static T t;
// 編譯前類型檢查報錯
public static T getT() {
return t;
}
}
泛型不允許直接進行類型轉換(通配符可以)
<code>List<integer> integerList = new ArrayList<integer>();
List<double> doubleList = new ArrayList<double>();
//不能直接進行類型轉換,類型檢查報錯
//integerList = doubleList;
/<double>/<double>/<integer>/<integer>/<code>
泛型不允許直接使用instanceof運算符進行運行時類型檢查(通配符可以)
<code>List<string> stringList = new ArrayList<string>();
//不能直接使用instanceof,類型檢查報錯
//LOGGER.info(stringList instanceof ArrayList<double>);
//我們可以通過通配符的方式進行instanceof運行期檢查(不建議):
//通過通配符實現運行時驗證LOGGER.info(stringList instanceof ArrayList>);/<double>/<string>/<string>/<code>
泛型不允許創建確切類型的泛型數組(通配符可以)
<code>//類型檢查報錯
//Demo6<integer>[] iDemo6s = new Demo6<integer>[2];/<integer>/<integer>/<code>
泛型不允許定義泛型異常類或者catch異常(throws可以)
- 不允許定義泛型異常類(直接或間接擴展Throwable類)
- 不允許捕獲一個泛型異常
- 可以以異常類作為邊界
- 可以throws泛型類型
泛型不允許作為參數進行重載
<code>static class Demo8{ /<code>
void test(List<integer> list){}
//不允許作為參數列表進行重載
//void test(List<double> list){}}/<double>/<integer>
參考 :
- https://blog.csdn.net/hanchao5272/article/details/79346471
- https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super
- https://guofeng007.com/2018/03/01/java-super-extends-template/#%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%8B%E7%95%8C
- https://blog.csdn.net/hanchao5272/article/details/79352321
閱讀更多 小盒子的技術分享 的文章