1、泛型的由來
我們先看下面這段代碼:
List list = new ArrayList(); list.add(24); //向集合中添加一個 Integer 類型的數據 list.add("Tom"); //向集合中添加一個 String 類型的數據 for(int i = 0 ; i < list.size() ; i++){ Object obj = list.get(i); //注意這里每個類型都是 Object System.out.println(obj); } //如果我們遍歷的時候就想得到自己想要的數據類型 for(int i = 0 ; i < list.size() ; i++){ String obj = (String) list.get(i); //在取 Integer 的時候會報類型轉換錯誤 System.out.println(obj); }
報錯信息如下:
也就是 集合中第二個數據是 Integer,但是我們取出來的時候將其轉換為 String 了,所以報錯。
那么這個如何解決呢?
①、我們在遍歷的時候,根據每個數據的類型判斷,然后進行強轉。
那么我們說這個集合只有兩條數據,我們可以進行判斷強轉,如果數據有成千上萬條呢,我們都通過這樣判斷強轉肯定不可取
②、在往集合中加入數據的時候,我們就做好限制,比如這個集合只能添加 String 類型的;下一個集合只能添加 Integer 類型的,那么我們在取數據的時候,由於前面已經限制了該集合的數據類型,那么就很好強轉了。
這第二種解決辦法,也就是我們這篇文章講的 泛型
2、什么是泛型?
泛型是Java SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。這種參數類型可以用在類、接口和方法的創建中,分別稱為泛型類、泛型接口、泛型方法。
在Java SE 1.5之前,沒有泛型的情況的下,通過對類型Object的引用來實現參數的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型可以預知的情況下進行的。對於強制類型轉換錯誤的情況,編譯器可能不提示錯誤,在運行的時候才出現異常,這是一個安全隱患。
3、泛型的基本用法
3.1 對於上面的問題我們只需要將上述代碼的 List list = new ArrayList() 改為 List<String> list = new ArrayList<String>();
List<String> list = new ArrayList<String>(); //list.add(22); //向集合中添加一個 Integer 類型的數據時,編譯器會報錯 list.add("Bob"); //向集合中添加一個 String 類型的數據 list.add("Tom"); //向集合中添加一個 String 類型的數據 //如果我們遍歷的時候就想得到自己想要的數據類型 for(int i = 0 ; i < list.size() ; i++){ String obj = list.get(i); //這里就不需要強轉了,前面添加的是什么類型,這里獲取的就是什么類型 System.out.println(obj); }
3.2 泛型是在編譯階段有效
List<String> list1 = new ArrayList<String>(); List list2 = new ArrayList(); Class c1 = list1.getClass(); Class c2 = list2.getClass(); System.out.println(c1==c2); //true
上述代碼,由於我們知道反射是在運行時階段,c1==c2為 true,說明了編譯之后的 class 文件中是不包含任意的泛型信息的。如果不信,我們可以看 class 文件的反編譯信息
java.util.List list1 = new ArrayList(); java.util.List list2 = new ArrayList(); Class c1 = list1.getClass(); Class c2 = list2.getClass(); System.out.println(c1.equals(c2));
我們可以看到 反編譯之后的 list1和 list2完全一樣。
結論:Java 泛型只在編譯階段有效,即在編譯過程中,程序會正確的檢驗泛型結果。而編譯成功后,class 文件是不包含任何泛型信息的
3.3 泛型類和泛型方法
public class Box<T> { private T box; public T getBox(T t){ this.box = t; return t; } public void getType(){ System.out.println("T的實際類型為:"+box.getClass().getName()); } public static void main(String[] args) { Box box = new Box(); System.out.println(box.getBox(1)); box.getType(); System.out.println(box.getBox("Tom")); box.getType(); } }
輸出結果為:
1 T的實際類型為:java.lang.Integer
Tom T的實際類型為:java.lang.String
3.4 泛型通配符
在泛型中,我們可以用 ? 來代替任意類型
public List wildCard(List<?> list){ return list; } public static void main(String[] args) { GenericTest gt = new GenericTest(); //構造一個 Interger 類型的集合 List<Integer> integer = new ArrayList<Integer>(); integer.add(1); System.out.println(gt.wildCard(integer)); //構造一個 String 類型的集合 List<String> str = new ArrayList<String>(); gt.wildCard(str); //構造一個 Object 類型的集合 List<Object> obj = new ArrayList<Object>(); obj.add(1); obj.add("a"); System.out.println(gt.wildCard(obj)); //構造一個 任意類型的 集合,這和 List<Object> 存放數據沒啥區別 List list = new ArrayList(); gt.wildCard(list); }
3.5 泛型的上限和下限
①、上限: 語法(? extends className),即只能為 className 或 className 的子類
//通配符的下限,只能是 Number 或 Number的子類 public List wildCard(List<? extends Number> list){ return list; } public static void main(String[] args) { GenericTest gt = new GenericTest(); //構造一個 Interger 類型的集合 List<Integer> integer = new ArrayList<Integer>(); integer.add(1); System.out.println(gt.wildCard(integer)); //構造一個 String 類型的集合 List<String> str = new ArrayList<String>(); //gt.wildCard(str); //編譯報錯 //構造一個 Object 類型的集合 List<Object> obj = new ArrayList<Object>(); obj.add(1); obj.add("a"); //System.out.println(gt.wildCard(obj)); //編譯報錯 }
①、下限: 語法(? super className),即只能為 className 或 className 的父類
//通配符的上限,只能是 Number 或 Number的父類 public List wildCard(List<? super Number> list){ return list; } public static void main(String[] args) { GenericTest gt = new GenericTest(); //構造一個 Interger 類型的集合 List<Integer> integer = new ArrayList<Integer>(); integer.add(1); //System.out.println(gt.wildCard(integer)); //編譯報錯 //構造一個 String 類型的集合 List<String> str = new ArrayList<String>(); //gt.wildCard(str); //編譯報錯 //構造一個 Object 類型的集合 List<Object> obj = new ArrayList<Object>(); obj.add(1); obj.add("a"); System.out.println(gt.wildCard(obj)); }
4、泛型的注意事項
4.1、不能用基本類型來定義泛型,如 int、float
List<int> list = new ArrayList<int>(); //不能用 int 這樣的基本類型定義泛型
關於這一點很好想明白,因為 集合中只能存放引用類型的數據,即使你存入基本類型的,Java還是會通過自動拆箱和自動裝箱機制將其轉換為引用類型
4.2、如果使用 ? 接收泛型對象時,則不能設置被泛型指定的內容
List<?> list = new ArrayList<>(); list.add("aa"); //錯誤,無法設置
4.3、泛型方法的定義與其所在的類是否是泛型類是沒有任何關系的,所在的類可以是泛型類,也可以不是泛型類
4.4、泛型類沒有繼承關系,即String 為 Object 類型的子類,則 List<String> 是 List<Object> 的子類這句話是錯誤的
原因:假設上面那句話是正確的,那么由於泛型的產生機制就是放什么類型的數據進去,取出來的就是什么類型,而不用進行類型轉換,這里把 String 類型的數據放入Object 類型的泛型集合中,那么取出來的應該就是 String 類型的數據,而實際上取出來的是 Object 類型的數據,這與泛型的產生機制相違背,故不成立!