三十四、泛型
1、概述
1.1 泛型含義
泛型是一種型別引數,專門用來儲存型別用的
最早接觸泛型是在ArrayList
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。若要呼叫特有方法需要轉型,給我們程式設計帶來麻煩。
使用泛型帶來的好處:
- 將執行時期的 Exception,轉移到了編譯時期的編譯失敗。
- 避免了型別強轉的麻煩
// 泛型沒有指定型別,預設是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 注意
- 泛型的程式碼在執行的時,泛型會被擦除。後面學習反射的餓時候,可以實現在程式碼執行的過程中新增其他型別的資料到集合
- 泛型裡面只能放引用資料型別
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);
}
泛型的確認:
-
可以在實現類實現介面時,確認介面中的泛型的型別
public class MyImp1 implements MyGenericInterface<String> { @Override public void add(String e) { // 省略... } @Override public String getE() { return null; } }
-
如果實現類和介面不指定具體的型別,繼續使用泛型指定,那就變成含有泛型的類使用
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 萬用字元基本使用
泛型萬用字元搭配集合使用一般在方法的引數中比較常見
方法中的引數是一個集合,集合如果攜帶了萬用字元,要特別注意如下:
- 集合的型別會提升為Object型別
- 方法中的引數是一個集合,集合如果攜帶了萬用字元,那麼此集合不能進行新增和修改操作,可以刪除和獲取。
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){}
泛型沒有繼承說法案例: