2016-07-11
未使用泛型舉例:
說明:任何Object子類均可以存入Object集合,但是從集合中獲取元素,則是Object對象,需要強制轉換,可能拋異常。
使用泛型能夠限定集合中存放的元素類型,標記集合中的元素類型,對存入集合的元素進行類型檢查,讓集合中的元素類型一致。泛型就是“參數化類型”,類比於形參和實參的關系,泛型就是讓原本具體類型進行參數化,在泛型具體化或者泛型調用的時候,將類型實參傳給類型形參,例如String這個實參傳給T形參。
public void test1(){ //未使用泛型 List list=new ArrayList(); list.add(1); //加入類型不一致 list.add("one"); for(int i=0;i<list.size();i++){ //可能出現ClassCastException int num=(int) list.get(i); } }
public void test2(){ //使用泛型,保證了集合使用的安全性 List<Integer> list=new ArrayList<>(); list.add(1); //不能加入其它類型 // list.add("one"); for(int i=0;i<list.size();i++){ //不會出現ClassCastException int num=list.get(i); } }
泛型類及方法的使用
class Animal<T>{ } //Cat繼承Animal //指定T為Short,Cat為具體類,不是泛型類 class Cat extends Animal<Short>{ } //泛型類Persion繼承Animal //還未指定T,Persion是泛型類 class Persion<T> extends Animal<T>{ private T t; List<T> list=new ArrayList<>(); public T getT(){ return this.t; } public void setT(T t){ this.t=t; } //泛型方法 public <E> E change(E e){ return e; } } public class Generic_Use{ public static void main(String[] args) { //泛型類使用 { //在實例化泛型類時,未指定泛型的具體類型,則泛型的默認類型為Object Persion persion=new Persion(); persion.setT("helloWorld"); //persion.getT() 返回Object類型 需要強制轉換為String String str=(String) persion.getT(); } { //在實例化泛型類時,指定了泛型的具體類型A,則所有出現泛型的地方均為類型A Persion<String> persion=new Persion<>(); persion.setT("helloWorld"); //persion.getT() 返回String類型 System.out.println(persion.getT()); { //泛型方法的具體化 //persion.change(11)均返回Integer類型 System.out.println(persion.change(11)); //通用的泛型方法具體化 System.out.println(persion.<Integer>change(11)); //出錯 無法指定類泛型具體類型為Integer的方法又用String進行 具體化 // System.out.println(persion.<Integer>change("12")); } } }
泛型接口的使用類似泛型類,可類比使用
public class Generic_extends { public static void main(String[] args) { { //普通類與繼承 Object object=null; String str="str"; object=str; Object[] objects=null; String[] strs=new String[]{"str1","str2"}; objects=strs; } { //泛型類與繼承 List<Object> list=null; List<String> list2=null; //出錯 List<String> 不是List<Object>的父類 // list=list2; { //證明 //反證:若List<Object>是 List<String>的父類 list.add(11); String str=list2.get(0);//會出錯 //結論:若A是B的父類,但是List<A>不是List<B>的父類 } } } }
原則:對於集合的使用,則是嚴進寬出,即放入集合要類型准確一致,取出可以模糊取出
注意點:靜態方法和catch塊不能使用泛型
建議:參考C++中的模板編程思想
public void test3(){ //通配符? { //List<?>是所有List<對象> 的父類 //List<?>可以指向任意的List<對象>實例對象,並取出其中的元素,但是不能放入入元素 List<?> list=new ArrayList<>(); List<Object> list2=new ArrayList<>(); List<String> list3=new ArrayList<>(); list2.add(12); list3.add("bbb"); list=list2; list=list3; { //list取出存在list3的元素 Iterator<?> iterator=list.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } //list不能直接通過自己向他的子類放入元素 //list向list3中存入元素 // list.add("ccc");//出錯 //特例 空對象 list.add(null); } } { //邊界通配符 ? extends A //允許集合中的元素類型是A及其子類(類型通配符上限 類比小於號《) List<? extends Number> list4=new ArrayList<>(); List<Integer> list5=new ArrayList<>(); list5.add(12); list4=list5; //list4取出存在list5中的元素 System.out.println(list4.get(0)); //list4向list5中存入元素 // list4.add(12);//出錯 } { //邊界通配符 ? super A //允許集合中的元素類型是A及其父類(類型通配符下限 類比大於號》) List<? super Integer> list6=new ArrayList<>(); List<Integer> list7=new ArrayList<>(); list7.add(12); list6=list7; //list6取出存在list7中的元素 System.out.println(list6.get(0)); //list6向list7中存入元素 // list6.add(12);//出錯 // list7=list6;//出錯 } //結論: //對於泛型,父類可以從子類中取元素,但是不能存元素到子類中 //或者說可以從通配符泛型類中取出元素,但是不能寫入元素 //體現了,集合中的元素嚴進寬出,寫入集合的元素需要具體類型,而讀取集合的元素沒有要求 } }
public void test4(){ List<Integer> list=new ArrayList<>(); List<String> list2=new ArrayList<>(); System.out.println("list的類型="+list.getClass()); System.out.println("list2的類型="+list2.getClass()); //結果: //list的類型=class java.util.ArrayList //list2的類型=class java.util.ArrayList }
從上面程序可以看出方法test4中list和list2的實際運行類型是一致的。
這關系到泛型的類型擦除。
原因分析:泛型設計的初衷就是在程序編寫的時候就能夠進行類型檢查,避免程序運行時出錯。對於泛型類型檢查通過後,所以在編譯時,會將泛型的相關信息擦出,也就是說,成功編譯過後的class文件中是不包含任何泛型信息的。其實就是好比C++中模板編程,所有同一個泛型的具體化對象實際均是同一類型,所有同一泛型的具體化對象其實是共享同一份代碼,即母代碼,可以類比java中類和實例之間的關系學習。