JAVA學習之泛型,java泛型
ArrayList<E>類定義和ArrayList<Integer>類引用中涉及的術語:
1、整個ArrayList<E>稱為泛型型別
2、ArrayList<E>中E稱為類型變數或型別參數
3、整個ArrayList<Integer>稱為參數化類別型
4、ArrayList<Integer>中的Integer叫型別參數的執行個體或實際型別參數
5、ArrayList<Integer>中的<>念typeof
6、ArrayList稱為原始類型
一、泛型是什麼能做什麼
泛型(Generic type 或者 generics)是對 Java 語言的類型系統的一種擴充,以支援建立可以按類型進行參數化類別。可以把型別參數看作是使用參數化型別時指定的類型的一個預留位置,就像方法的形式參數是運行時傳遞的值的預留位置一樣。
JDK1.5 之前使用集合的問題
1 public static void testJDK5Befor(){2 List list=new ArrayList();3 list.add(1);4 list.add(2);5 list.add("abc");6 int a1=(int) list.get(1);7 int a2=(int) list.get(2);8 }
執行代碼在第7行出現錯誤 Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
JDK1.5 對集合加入了泛型的概念,直接在編譯階段驗證資料類型。
1 public static void testJDK5After(){2 List<Integer> list=new ArrayList<Integer>();3 list.add(1);4 list.add(2);5 list.add("abc");6 }
第5行在編寫時直接報錯,提醒你:The method add(Integer) in the type List<Integer> is not applicable for the arguments (String)
泛型的作用:泛型在編譯時間期進行嚴格的類型檢查,消除了絕大多數的類型轉換。泛型在集合中使用廣泛,在JDK1.5之後集合架構就全部加入了泛型支援。在沒有使用泛型之前,我們可以往List集合中添加任何類型的元素資料,因為此時List集合預設的元素類型為Object,而在我們使用的時候需要進行強制類型轉換,這個時候如果我們往List中加入了不同類型的元素,很容易導致類型轉換異常。
二、泛型擦除
泛型只作用於編譯階段,在編譯階段嚴格檢查類型是否匹配,類型檢查通過後,JVM會將泛型的相關資訊擦出掉(即泛型擦除),也就是說,成功編譯過後的class檔案中是不包含任何泛型資訊的,泛型資訊不會進入到運行時階段。
public static void testMain01(){ ArrayList <String> listStr=new ArrayList<String>(); ArrayList <Integer> listInt=new ArrayList<Integer>(); System.out.println(listInt.getClass()==ArrayList.class);//true System.out.println(listStr.getClass()==listInt.getClass());//true }
通過該例子我們可以看出泛型在編譯為.class檔案,載入到記憶體中後是沒有泛型概念的。
1、通過泛型的該特性我們可以使用反射繞過泛型檢查向集合中添加任意類型的資料
public static void testMain02() throws Exception{ ArrayList <Integer> listInt=new ArrayList<Integer>(); listInt.add(123); //我們要往該集合內添加其他類型的資料 listInt.getClass().getMethod("add", Object.class).invoke(listInt, "string"); System.out.println(listInt.size());//2 System.out.println(listInt.get(1));//string } 三、泛型使用中出現的問題
//參數化型別與原始類型的相容性-編譯警告Collection<String> = new Vector();//原始類型可以引用一個參數化型別的對象。Collection = new Vector<String >();//參數化型別不考慮型別參數的繼承關係Vector<String> v = new Vector<Object>(); //錯!Vector<Object> v = new Vector<String>();//錯!//在建立數組執行個體時,數組的元素不能使用參數化類別型Vector<Integer> v[] = new Vector<Integer>[10];//編譯可以通過!因為編譯器只會按行解釋Vector v1 = new Vector<Integer>();Vector<Object> v = v1;
四、泛型的?萬用字元及擴充<?>可以引用各種參數化類別型,可以調用與參數無關的方法,不能調用與參數有關的方法
/** * 遍曆任意集合 * @param collection */ public static void printCollection(Collection<?> collection){ //System.out.println(collection.add("aa")); System.out.println(collection.size()); for(Object obj:collection){ System.out.println(obj); } }
限定萬用字元的上邊界
正確:Vector<? extends Number> v=new Vector<Integer>();表示 該泛型型別必須是number類型或者其子類類型
錯誤:Vector<? extends Number> v=new Vector<String>();
限定萬用字元的下邊界
正確:Vector<? super Integer> v=new Vector<Number>();表示 該泛型型別必須是Integer類型或者Integer的父類類型
錯誤:Vector<? extends Integer > v=new Vector<Byte>();
五、定義泛型的方法
//採用自訂泛型的方式列印出任意參數類型中的所有元素 private static<T> void printCollection(Collection<T> collection,T obj2 ){ for(T obj : collection){ System.out.println(obj); } collection.add(obj2); } //定義一個 方法,可以將任意類型的某個數組填充為相應類型的某個對象 private static<T> void fillArray(T[] a,T obj){ for(int i=0;i<a.length;i++){ a[i] = obj; } } //定義一個 方法,自動將Object類型的對象轉換成其它類型 private static<T> T autoConvert(Object obj){ return (T)obj; }六、自訂泛型類
1 package com.jalja.org.base.test; 2 3 import java.util.Date; 4 5 public class GenericDao { 6 public <T> void add(T t){ 7 8 } 9 public <T> T findById(int id){10 return null;11 }12 13 public static void main(String[] args) {14 GenericDao gd=new GenericDao();15 gd.add(new Date());16 String str=gd.findById(12);17 }18 }
一般在DAO中,每個DAO操作的是同一個資料類型,而在本例的15行添加date類型,在16行確返回了一個string類型,我們如何控制我們的dao操作的是同一類型呢,使用泛型類
1 package com.jalja.org.base.test; 2 3 import java.util.Date; 4 5 public class GenericDao <T>{ 6 public void add(T t){ 7 System.out.println(t.getClass()); 8 } 9 public T findById(int id){10 return null;11 }12 13 public static void main(String[] args) {14 GenericDao<Date> gd=new GenericDao<Date>();15 gd.add(new Date());16 String str=(String) gd.findById(12);17 }18 }
在這裡會發現16行出錯,Cannot cast from Date to String 。應為你必須操作同一類型 date
泛型類中的靜態方法:
package com.jalja.org.base.test;import java.util.Date;public class GenericDao <T>{ public void add(T t){ System.out.println(t.getClass()); } public T findById(int id){ return null; } //泛型中不能出現與泛型類相關的靜態方法,應為靜態方法是作用於類層級的,與具體類的執行個體對象沒有關係。 //如果使用類名調用,泛型型別參數T是無法確定具體類型的所以,這樣不可以。 public static void update(T t){ } //可以定義與泛型類的泛型型別參數無關的靜態方法 public static <E> void update(E e){ }}