1. 程式人生 > 其它 >三十四、泛型

三十四、泛型

1、概述

1.1 泛型含義

泛型是一種型別引數,專門用來儲存型別用的

最早接觸泛型是在ArrayList,這個 E 就是所謂的泛型了。使用ArrayList時,只要給 E 指定某一個型別,裡面所有用到泛型的地方都會被指定對應的型別。

ArrayList<String> list1 = new ArrayList<>();    // E = String

ArrayList<Integer> list2 = new ArrayList<>();    // E = Integer

ArrayList list3 = new ArrayList<>();      // E = Object
如果沒有給泛型變數設定一個型別,預設表示Object。

1.2 使用泛型的好處

不用泛型帶來的問題:

集合若不指定泛型,預設就是Object。儲存的元素型別自動提升為Object型別。獲取元素時得到的都是Object。若要呼叫特有方法需要轉型,給我們程式設計帶來麻煩。

使用泛型帶來的好處:

  1. 將執行時期的 Exception,轉移到了編譯時期的編譯失敗。
  2. 避免了型別強轉的麻煩
// 泛型沒有指定型別,預設是Object
ArrayList arr = new ArrayList();

arr.add("Hello");
arr.add("World");
arr.add(100);
arr.add(false);

// 集合中的資料就比較混亂,會給獲取資料帶來麻煩
for (Object o : arr) {
    String str = (String)o;

    // 當遍歷到非String類資料,就會報異常
    // java.lang.ClassCastException
    System.out.println(str);
}

1.3 注意

  1. 泛型的程式碼在執行的時,泛型會被擦除。後面學習反射的餓時候,可以實現在程式碼執行的過程中新增其他型別的資料到集合
  2. 泛型裡面只能放引用資料型別

2、泛型的定義和使用

泛型,用來靈活的將資料型別應用到不同的類、介面、方法中。將資料型別作為引數進行傳遞。

2.1 自定義泛型類

使用場景:

當一個類中定義屬性的時候,不確定屬性具有什麼型別時,就可以使用泛型表示該屬性的型別

定義格式:

在型別名後面加上一對尖括號,裡面定義泛型,一般使用一個英文大寫字母表示,如果有多個泛型,使用逗號分隔。

許可權修飾符 class 類名<泛型名>{
    型別內部,就可以把泛型名當做是某一種型別使用了。
}

public class Student<X,Y>{
    X xObj;
}

泛型的確認:

在建立物件的時候,確認泛型類中泛型的資料型別

ArrayList<String> list = new ArrayList<>();

2.2 自定義泛型介面

使用場景:

當定義介面時,內部方法中其引數型別,返回值型別不確定時,就可以使用泛型替代了。

定義格式:

許可權修飾符 interface 介面名<泛型名>{
    型別內部,就可以把泛型名當做是某一種型別使用了。
}

public interface Collection<E>{
    public boolean add(E e);
}

泛型的確認:

  1. 可以在實現類實現介面時,確認介面中的泛型的型別

    public class MyImp1 implements MyGenericInterface<String> {
    	@Override
        public void add(String e) {
            // 省略...
        }
    
    	@Override
    	public String getE() {
    		return null;
    	}
    }
    
  2. 如果實現類和介面不指定具體的型別,繼續使用泛型指定,那就變成含有泛型的類使用

    public class MyImp2<E> implements MyGenericInterface<E> {
    	@Override
    	public void add(E e) {
           	 // 省略...
    	}
    
    	@Override
    	public E getE() {
    		return null;
    	}
    }
    
    /*
     * 使用
     */
    public class GenericInterface {
        public static void main(String[] args) {
            MyImp2<String>  my = new MyImp2<String>();  
            my.add("aa");
        }
    }
    

2.3 自定義泛型方法

使用場景:

當定義方法時,方法中引數型別,返回值型別不確定時,就可以使用泛型替代了

定義格式:

可以在方法的返回值型別前,加上泛型

許可權修飾符 <泛型名> 返回值型別 方法名(引數列表){
    ....
} 

public interface Collection<E> extends Iterable<E>{
    ...
    <T> T[] toArray(T[] a);     //將集合變成陣列
}

泛型的指定:

呼叫含有泛型的方法時,傳入的資料其型別就是泛型的型別

3、泛型萬用字元

當我們對泛型的型別確定不了,而想要表達的可以是任意型別,可以使用泛型萬用字元給定。符號就是一個問號: 表示任意型別,用來給泛型指定的一種通配值。

3.1 萬用字元基本使用

泛型萬用字元搭配集合使用一般在方法的引數中比較常見

方法中的引數是一個集合,集合如果攜帶了萬用字元,要特別注意如下:

  1. 集合的型別會提升為Object型別
  2. 方法中的引數是一個集合,集合如果攜帶了萬用字元,那麼此集合不能進行新增和修改操作,可以刪除和獲取。
public static void main(String[] args) {
    ArrayList<Number>  list1 = new ArrayList<>();
    ArrayList<Object>  list2 = new ArrayList<>();
    ArrayList<String>  list3 = new ArrayList<>();
    ArrayList<Integer> list4 = new ArrayList<>();
    
    // ... 新增資料
    
    useList3(list1);
    useList3(list2);
    useList3(list3);
    useList3(list4);
}
// 方法的引數是一個List , 泛型為萬用字元 , 那麼此引數可以接收任意型別泛型的List集合
public static void useList3(ArrayList<?> list){
    // 方法中的引數是一個集合,集合如果攜帶了萬用字元 , 集合中的元素預設是Object型別
    // Object o = list.get(0);

    // 方法中的引數是一個集合,集合如果攜帶了萬用字元 , 那麼此集合不能進行新增和修改操作 , 可以刪除和獲取
    // list.add(new Object());
    // list.add("");
    // list.add(10);
    // list.set(0 , "");

    System.out.println(list);
    list.remove(1);
    Object o = list.get(1);
}

3.2 受限泛型

介紹:

受限泛型是指,在使用萬用字元的過程中,對泛型做了約束,給泛型指定型別時,只能是某個型別父型別或者子型別。

  • 泛型的下限

<? super 型別> :只能是某一型別,及其父型別,其他型別不支援

  • 泛型的上限

<? extends 型別> : 只能是某一型別,及其子型別,其他型別不支援

【程式碼體現】
public static void main(String[] args) {
    Collection<Integer> list1 = new ArrayList<Integer>();
    Collection<String> list2 = new ArrayList<String>();
    Collection<Number> list3 = new ArrayList<Number>();
    Collection<Object> list4 = new ArrayList<Object>();
    
    getElement1(list1);
    getElement1(list2);//報錯
    getElement1(list3);
    getElement1(list4);//報錯
  
    getElement2(list1);//報錯
    getElement2(list2);//報錯
    getElement2(list3);
    getElement2(list4);
}
// 泛型的上限:此時的泛型?,必須是Number型別或者Number型別的子類
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此時的泛型?,必須是Number型別或者Number型別的父類
public static void getElement2(Collection<? super Number> coll){}

泛型沒有繼承說法案例: