一、泛型的由來java
咱們先看下面這段代碼:安全
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); }
報錯信息以下:this
也就是 集合中第二個數據是 Integer,可是咱們取出來的時候將其轉換爲 String 了,因此報錯。spa
那麼這個如何解決呢?對象
①、咱們在遍歷的時候,根據每一個數據的類型判斷,而後進行強轉。blog
那麼咱們說這個集合只有兩條數據,咱們能夠進行判斷強轉,若是數據有成千上萬條呢,咱們都經過這樣判斷強轉確定不可取繼承
②、在往集合中加入數據的時候,咱們就作好限制,好比這個集合只能添加 String 類型的;下一個集合只能添加 Integer 類型的,那麼咱們在取數據的時候,因爲前面已經限制了該集合的數據類型,那麼就很好強轉了。接口
這第二種解決辦法,也就是咱們這篇文章講的 泛型開發
二、什麼是泛型?get
泛型是Java SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。這種參數類型能夠用在類、接口和方法的建立中,分別稱爲泛型類、泛型接口、泛型方法。
在Java SE 1.5以前,沒有泛型的狀況的下,經過對類型Object的引用來實現參數的「任意化」,「任意化」帶來的缺點是要作顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型能夠預知的狀況下進行的。對於強制類型轉換錯誤的狀況,編譯器可能不提示錯誤,在運行的時候纔出現異常,這是一個安全隱患。
三、泛型的基本用法
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.一、不能用基本類型來定義泛型,如 int、float
List<int> list = new ArrayList<int>(); //不能用 int 這樣的基本類型定義泛型
關於這一點很好想明白,由於 集合中只能存放引用類型的數據,即便你存入基本類型的,Java仍是會經過自動拆箱和自動裝箱機制將其轉換爲引用類型
4.二、若是使用 ? 接收泛型對象時,則不能設置被泛型指定的內容
List<?> list = new ArrayList<>(); list.add("aa"); //錯誤,沒法設置
4.三、泛型方法的定義與其所在的類是不是泛型類是沒有任何關係的,所在的類能夠是泛型類,也能夠不是泛型類
4.四、泛型類沒有繼承關係,即String 爲 Object 類型的子類,則 List<String> 是 List<Object> 的子類這句話是錯誤的
緣由:假設上面那句話是正確的,那麼因爲泛型的產生機制就是放什麼類型的數據進去,取出來的就是什麼類型,而不用進行類型轉換,這裏把 String 類型的數據放入Object 類型的泛型集合中,那麼取出來的應該就是 String 類型的數據,而實際上取出來的是 Object 類型的數據,這與泛型的產生機制相違背,故不成立!