泛型的使用
- 2019 年 10 月 3 日
- 筆記
一,泛型概述
關於泛型,先來說幾句集合。都知道集合是可以存儲任意對象,當我們創建一個集合時如果沒有聲明它的存儲類型,那該集合便自動提升為Object類型。請參看如下程式碼:
public class GenericDemo { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("abc"); coll.add("hello"); coll.add(5);//由於集合沒有做任何限定,任何類型都可以給其中存放 // 使用迭代器遍歷集合 Iterator it = coll.iterator(); while(it.hasNext()){ //需要列印每個字元串的長度,就要把迭代出來的對象轉成String類型 String str = (String) it.next(); System.out.println(str.length()); } } } 毫無疑問,以上程式碼會報錯,首先從程式碼上看在集合中存儲了數值類型,字元串類型。但是在使用迭代器遍歷時,取出的是String類型的,那麼對於數值類型來說就必然會報錯。

那麼以上問題該怎麼解決呢?通常對於Collection集合來說,同一個集合對象只保存一種數據類型。如下程式碼所示:
// 存儲字元類型 Collection<String> stringCollection = new ArrayList<>(); // 存儲數值類型 Collection<Integer> integerCollection = new ArrayList<>(); // 存儲精度類型 Collection<Double> doubleCollection = new ArrayList<>(); 也就是說在我們初始化集合的時候,就已經將存儲存儲類型固定。但是按照Java的靈活性來說,這是顯然不夠的,比如預先並不確定要存儲的類型。因此在JDK5之後新增了泛型的語法,使我們的程式顯得更為靈活。
泛型:在方法或者類(介面)中預先的使用某種未知的數據類型。
提示:在我們創建對象的時候,如果沒有明確指出一種數據類型,那麼編譯器會默認為Object類。
1.1,泛型的意義
那說了這麼多,泛型又能解決什麼問題呢?針對上面的案例分析:
1,可以將ClassCastException異常由運行期轉到編譯期異常。
2,避免強制類型的轉換。

請看如上程式碼,在我們初始化集合時指定String數據類型,此時再存儲數值類型就會在編譯期報錯,那麼泛型又將如何運用呢。先別急,我們先看看集合中是如何定義的。

在Array List集合源碼中看出,它的存儲類型是一個E,這是什麼呢?
- E,Elements:元素
- T,Type:類型
這就是泛型,因為我們對於集合來說它們並不知道我們要存儲是什麼類型,因此這就是泛型的意義。
提示:泛型是數據類型的一部分,我們將類名與泛型合併一起看做數據類型。
泛型,用來靈活地將數據類型應用到不同的類、方法、介面當中。將數據類型作為參數進行傳遞。
二,泛型類
編寫格式:
修飾符 class 類名稱<泛型變數>{} 我們還是以集合為例,請看如下集合源程式碼實現,以add方法作為參考。

可以看出add方法接收的數據類型為E,與我們上一節圖片中所示Array List源碼實現是一致的,因為Array List接收的參數類型就是E。說這麼多有些啰嗦,現在先定義一個泛型類。
public class GenericClass<T> { private T key; public void setKey(T key){ this.key = key; } public T getKey(){ return key; } } public class GenericDemo { public static void main(String[] args) { GenericClass<String> genericString = new GenericClass<>(); // 存儲字元類型 genericString.setKey("這是一個泛型類"); // 獲取數據 String stringKey = genericString.getKey(); System.out.println(stringKey); // 存儲數值類型 GenericClass<Integer> genericInteger = new GenericClass<>(); // 存儲數值1 genericInteger.setKey(1); // 獲取數值 Integer integerKey = genericInteger.getKey(); System.out.println(integerKey); } } 
三,泛型方法
編寫格式:
修飾符 <泛型變數> 返回值類型 方法名稱(參數){} 例如定義泛型方法:
public class GenericMethod { // 定義泛型方法 public <T> void menthod(T t) { System.out.println(t.getClass()); } // 返回實例化對象 public <T> T getGenericMethod(Class<T> t) throws IllegalAccessException, InstantiationException { return t.newInstance(); } public static void main(String[] args) throws Exception { GenericMethod genericMethod = new GenericMethod(); genericMethod.menthod("abc"); Object method = genericMethod.getGenericMethod(Class.forName("com.api.generic.User")); System.out.println(method); } } 四,泛型介面
編寫格式:
修飾符 interface 介面名稱<泛型變數>{} 定義泛型介面:
public interface GenericInterface<T> { void add(T t); T getT(); } 如果泛型介面的實現類仍不確定,則還可以接著使用泛型。
public class GenericInterfaceImpl<T> implements GenericInterface<T> { @Override public void add(T t) { } @Override public T getT() { return null; } } 只有在創建對象的時候,要確定泛型的數據類型。
GenericInterfaceImpl<String> genericInterface = new GenericInterfaceImpl<>(); genericInterface.add("Hello"); 五,泛型通配符
當使用泛型類或者介面時,傳遞的數據中,泛型類型不確定,可以通過通配符<?>表示。但是一旦使用泛型的通配符後,只能使用Object類中的共性方法,集合中元素自身方法無法使用。
通配符基本使用
泛型的通配符:不知道使用什麼類型來接收的時候,此時可以使用?,?表示未知通配符。
此時只能接收數據,不能往該集合中存儲數據。
舉個例子大家理解使用即可:
public static void main(String[] args) { Collection<Intger> list1 = new ArrayList<Integer>(); getElement(list1); Collection<String> list2 = new ArrayList<String>(); getElement(list2); } public static void getElement(Collection<?> coll){} //?代表可以接收任意類型 注意:泛型不存在繼承關係 Collection<Object> list = new ArrayList<String>();這種是錯誤的。 六,泛型上下限
之前設置泛型的時候,實際上是可以任意設置的,只要是類就可以設置。但是在JAVA的泛型中可以指定一個泛型的上限和下限。
泛型的上限:
- 格式:
類型名稱 <? extends 類 > 對象名稱 - 意義:
只能接收該類型及其子類
泛型的下限:
- 格式:
類型名稱 <? super 類 > 對象名稱 - 意義:
只能接收該類型及其父類型
比如:現已知Object類,String 類,Number類,Integer類,其中Number是Integer的父類
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>(); getElement(list1); getElement(list2);//報錯 getElement(list3); getElement(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){} 七,泛型擦除
在泛型的使用中還有一項重要的概念,就是泛型擦除。
這裡就不再分享了,有一篇寫的很好的部落格關於泛型擦除的,有興趣的可以去看看。
轉載自:https://blog.csdn.net/briblue/article/details/76736356
八,總結
總體來說泛型的出現為我們的程式碼帶來很多便利,使程式更加靈活。並且在官方文檔中提出如果能使用泛型就盡量使用泛型,提高程式碼的可讀性。
這次關於泛型的總結相對來說並不深入,只是一些簡單的描述。最後,在本文中的內容如有不適之處,歡迎多多指點。
感謝閱讀!