關於java泛型你應該知道的那些事兒

泛型的設計初衷:是為了減少類型轉換錯誤產生的安全隱患,而不是為了實現任意化。


泛型可以應用在類、接口和方法的創建中,分別稱為泛型類、泛型接口和泛型方法


  • 泛型類在類名後面用尖括號表示泛型
<code>public class HelloWorld {
    private T t;
public T getValue() {
return t;    
}
public void setValue(T t) {
this.t = t;
}
}
/<code>
  • 泛型方法在方法聲明中加入泛型
<code>//這是泛型方法
static void printHelloWorld(T t){
LOGGER.info(t);
}
//這是一個普通方法
public T getT() {
return t;
}
/<code>
  • 泛型接口跟類類似
<code>public interface Generator {  
public T next();
}
/<code>

總結:

  • 帶尖括號的 才能表示是泛型類或方法或接口。而只有T的只能表示是泛型類型
  • 泛型類型的命名並不是必須為T,也可以使用其他字母,如X、K等,只要是命名為單個大寫字即可。
  • 雖然沒有強制的命名規範,但是為了便於代碼閱讀,也形成了一些約定俗成的命名規範,如下:

T通用泛型類型,通常作為第一個泛型類型S

通用泛型類型,如果需要使用多個泛型類型,可以將S作為第二個泛型類型

U通用泛型類型,如果需要使用多個泛型類型,可以將U作為第三個泛型類型V通用泛型類型,如果需要使用多個泛型類型,可以將V作為第四個泛型類型E集合元素 泛型類型,主要用於定義集合泛型類型K映射-鍵 泛型類型,主要用於定義映射泛型類型V映射-值 泛型類型,主要用於定義映射泛型類型N數值 泛型類型,主要用於定義數值類型的泛型類型


通配符、上下邊界、無界

如果不對泛型類型做限制,則泛型類型可以實例化成任意類型,這種情況可能產生某些安全性隱患。

為了限制允許實例化的泛型類型,我們需要一種能夠限制泛型類型的手段,即:有界泛型類型

<code>//有界泛型類型語法 - 繼承自某父類
//有界泛型類型語法 - 實現某接口
//有界泛型類型語法 - 多重邊界
//示例//N標識一個泛型類型,其類型只能是Number抽象類的子類
//T標識一個泛型類型,其類型只能是Person類型的子類,並且實現了Comparable 和 Map接口
/<code>

上邊界通配符( extends 父類型>)

  • 上界:用 extends 關鍵字聲明,表示參數化的類型可能是所指定的類型,或者是此類型的子類。
  • 類型參數列表中如果有多個類型參數上限,用逗號分開private E test(K arg1, E arg2){}
  • 上邊界類型通配符可以確定父類型
  • 獲取數據時,由於固定了上界,類型肯定是返回類型的子類型,可以通過向上轉型成功獲取。
  • 寫入數據時,由於向下轉型存在很大風險,Java泛型為了減低類型轉換的安全隱患,不允許這種操作。
關於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>
關於java泛型你應該知道的那些事兒

<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對象才能裝下。但這樣的話,元素的類型信息就全部丟失。

  1. 頻繁往外讀取內容的,適合用上界Extends。
  2. 經常往裡插入的,適合用下界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>// 編譯前類型檢查報錯
List list = new List();/<code>

泛型不允許進行實例化

<code> void test(T t){    
//編譯前類型檢查報錯
t = new T();
}
/<code>

泛型不允許進行靜態化

<code>static class Demo{        
// 編譯前類型檢查報錯
private static T t;
// 編譯前類型檢查報錯
public static T getT() {
return t;
}    
}    
/<code>

泛型不允許直接進行類型轉換(通配符可以)

<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{   
void test(List<integer> list){}
//不允許作為參數列表進行重載
//void test(List<double> list){}}/<double>/<integer>
/<code>

參考 :

  • 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


分享到:


相關文章: