標籤:... 防止 ide string類 對象 介面 類型 nod 重要
java泛型實現了“參數化型別”的概念,所謂“參數化型別”是指將操作的資料類型指定為一個參數,這點在容器中用的最多,例如:List<String> strlist=new ArrayList<String>(),List<Integer> intlist=new ArrayList<Integer>();strlist可以操作的資料類型是String類型,intlist可以操作的資料類型是int類型,泛型在容器中得到了充分的應用。
泛型是使得代碼重用得到很大的提升,我們在程式中也會想一些辦法設計代碼重用的方法,例如輸出一個內容,我們要用System.out.println(...),敲這麼多東西還是麻煩,我們可以定義一個println()方法,如下:
static void println(Object o){
System.out.println(o);
}
再在程式中輸出東西時,只需調用println(...)即可,這個println()方法就有了“泛化”的機制,它可以輸出任何System.out.println()可以輸出的東西,該方法的參數類型被設定為基類Object類型,我們知道,Java中的基礎資料型別 (Elementary Data Type)都有相對應的類類型的資料,比如int類型的資料對應的有Integer類類型的資料,java有“自動裝箱”功能,也就是將對應的基本類型的資料轉化為對應類類型的對象,例如上面的println(3),在編譯後會變成println(Integer.valueOf(3));而所有的java類都是繼承自Object類,所以當然可以用println()方法了。
那麼將資料類型都定義為Object類型不就可以設計一個泛化類型的方法,類和介面了嗎?答案是可以的,但是“太泛化”了,以至於沒了邊界。這裡先給出代碼:
1 public class gen{ 2 3 public static void main(String[] args){ 4 myList list=new myList(); 5 list.push("helloworld"); 6 list.push(12); 7 Object val; 8 while((val=list.pop())!=null){ 9 System.out.println(val.getClass());10 }11 }12 }13 14 class myList{ 15 class Node{16 Object val=null;17 Node next=null;18 Node(Object val,Node next){19 this.val=val;20 this.next=next;21 }22 Node(){23 val=null;24 next=null;25 }26 boolean end(){27 return val==null && next==null;28 }29 }30 private Node top=null;31 myList(){32 top=new Node();33 }34 //插入新值35 public void push(Object val){36 top=new Node(val,top);37 }38 //返回頭結點的值39 public Object pop(){40 Object val=top.val;41 if(!top.end()){42 top=top.next;43 }44 return val;45 }46 }View Code
我們自訂了一個list鏈表,為了使這個鏈表有更好的普遍使用性,我們將鏈表類內部的資料定義為Object類型,這樣我們可以插入String類型資料、int類型資料....。但是這個設計“太泛化”了,你甚至可以“混插”,比如上面的執行結果,int類型的資料和String類型的資料插到了一起,顯然在後續的操作中可能出現錯誤,例如你建立了一個自以為全是int類型的鏈表並對它們求和,但是不小心插入了字串類型的數字"123",程式編譯也能通過,你以為你的程式是對的,結果啟動並執行時候出錯了!你深受打擊,自掛東南枝,全球電腦行業從此一蹶不振.....
為了防止上面悲劇的發生,我們還是希望錯誤在編譯時間期就能暴露出來,以便將錯誤及時地扼殺與萌芽狀態,泛型設計的一項重大功能體現出來了:提前指定我們的鏈表可以插入什麼樣的資料類型,並由編譯器驗證其類型的正確性!這時,我們可以這樣設計:
1 public class gen{ 2 public static void main(String[] args){ 3 //這裡指定list2中的資料類型為String類型,也就是將T設定為String類型 4 myList2<String> list2=new myList2<String>(); 5 list2.push("hello world"); 6 list2.push("hello code"); 7 //list2.push(23);//編譯時間便會提醒錯誤 8 String s; 9 while((s=list2.pop())!=null){10 System.out.println(s);11 }12 }13 }14 class myList2<T>{15 class Node<U>{16 U val=null;17 Node<U> next=null;18 Node(U val,Node<U> next){19 this.val=val;20 this.next=next;21 }22 Node(){23 val=null;24 next=null;25 }26 boolean end(){27 return val==null && next==null;28 }29 }//這裡將Node中的實值型別設定為U類型,也就是將U傳遞給T30 private Node<T> top=null;31 myList2(){32 top=new Node<T>();33 }34 public void push(T val){35 top=new Node<T>(val,top);36 }37 public T pop(){38 T val=top.val;39 if(!top.end()){40 top=top.next;41 }42 return val;43 }44 }View Code
當我們不小心執行list2.push(23)時,編譯器在編譯階段就會提醒:你錯啦!於是我們很愉快地就可以將錯誤的代碼修正過來。
Java容器類可以說是Java代碼重用設計的一個重要體現,而Java容器類設計的基礎就是泛型,這就是Java泛型的重要之處。
註:以上代碼設計部分參考自《Thinking In Java》,感謝Bruce Eckel。
java編程之泛型