J2SE 5.0 提供的泛型,目的在讓您定義「安全的」泛型類別(Generics class),事實上 J2SE 5.0 前就用 Object 解決了泛型類別的部份需求,J2SE 5.0 之後再解決的是型態安全問題.
沒有泛型之前 :
考慮您要設計下面的兩個類別(兩個很無聊的類別,但足以說明需求):
- BooleanFoo.java
- public class BooleanFoo {
- private Boolean foo;
- public void setFoo(Boolean foo) {
- this.foo = foo;
- }
- public Boolean getFoo() {
- return foo;
- }
- }
- public class IntegerFoo {
- private Integer foo;
- public void setFoo(Integer foo) {
- this.foo = foo;
- }
- public Integer getFoo() {
- return foo;
- }
- }
- ObjectFoo.java 代碼 :
- public class ObjectFoo {
- private Object foo;
- public void setFoo(Object foo) {
- this.foo = foo;
- }
- public Object getFoo() {
- return foo;
- }
- }
- ObjectFoo foo1 = new ObjectFoo();
- ObjectFoo foo2 = new ObjectFoo();
- foo1.setFoo(new Boolean(true));
- // 記得轉換介面
- Boolean b = (Boolean) foo1.getFoo();
- // 記得轉換介面
- foo2.setFoo(new Integer(10));
- Integer i = (Integer) foo2.getFoo();
定義泛型類別 :
當您定義類別時,發現到好幾個類別的邏輯其實都相同,就只是當中所涉及的型態不一樣時,使用複製、貼上、取代的功能來撰寫程式只是讓您增加不必要的檔案管理困擾. 由於Java中所有定義的類別,都以 Object 為最上層的父類別,所以在 J2SE 5.0 之前,Java程式設計人員可以使用 Object 來解決上面這樣的需求,為了讓定義出來的類別可以更加通用(Generic),傳入的值或傳回的物件都是以 Object 為主,當您要取出這些物件來使用時,必須記得將介面轉換為原來的類型,這樣才可以操作物件上的方法.
在 J2SE 5.0 之後,提出了針對泛型(Generics)設計的解決方案,要定義一個簡單的泛型類別是簡單的,直接來看個例子 :
- GenericFoo.java 代碼 :
- public class GenericFoo<T> {
- private T foo;
- public void setFoo(T foo) {
- this.foo = foo;
- }
- public T getFoo() {
- return foo;
- }
- }
- GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
- GenericFoo<Integer> foo2 = new GenericFoo<Integer>();
- foo1.setFoo(new Boolean(true));
- Boolean b = foo1.getFoo();
- foo2.setFoo(new Integer(10));
- Integer i = foo2.getFoo();
- GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
- foo1.setFoo(new Boolean(true));
- Integer b = foo1.getFoo(); //編譯時發生錯誤
回過頭來看看下面的宣告 :
- GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
- GenericFoo<Integer> foo2 = new GenericFoo<Integer>();
幾個定義泛型的例子 :
您可以在定義泛型類別時,宣告多個類型持有者,例如 :
- package gossip.generic;
- public class GenericFoo<T1,T2> {
- private T1 foo1;
- private T2 foo2;
- public void setFoo1(T1 foo1) {
- this.foo1 = foo1;
- }
- public T1 getFoo1() {
- return foo1;
- }
- public void setFoo2(T2 foo2) {
- this.foo2 = foo2;
- }
- public T2 getFoo2() {
- return foo2;
- }
- }
- GenericFoo<Integer,Boolean> genericFoo = new GenericFoo<Integer,Boolean>();
- public class GenericFoo<T> {
- private T foo;
- public void setFoo(T foo) {
- this.foo = foo;
- }
- public T getFoo() {
- return foo;
- }
- }
- package gossip.generic;
- public class WrapperFoo<T> {
- private GenericFoo<T> foo;
- public void setFoo(GenericFoo<T> foo) {
- this.foo = foo;
- }
- public GenericFoo<T> getFoo() {
- return foo;
- }
- }
- GenericFoo<Integer> foo = new GenericFoo<Integer>();
- foo.setFoo(new Integer(10));
- WrapperFoo<Integer> wrapper = new WrapperFoo<Integer>();
- wrapper.setFoo(foo);
在定義泛型類別時,預設您可以使用任何的型態來實例化泛型類別中的型態持有者,但假設您想要限制使用泛型類別時,只能用某個特定型態或其子類別才能實例化型態持有者的話, 您可以在定義型態持有者時,一併使用 "extends" 指定這個型態持有者必須是擴充某個類型,舉個實例來說 :
- ListGenericFoo.java 代碼 :
- package gossip.generic;
- import java.util.List;
- public class ListGenericFoo<T extends List> {
- private T[] fooArray;
- public void setFooArray(T[] fooArray) {
- this.fooArray = fooArray;
- }
- public T[] getFooArray() {
- return fooArray;
- }
- }
- ListGenericFoo<LinkedList> foo1 = new ListGenericFoo<LinkedList>();
- ListGenericFoo<ArrayList> foo2 = new ListGenericFoo<ArrayList>();
- ListGenericFoo<HashMap> foo3 = new ListGenericFoo<HashMap>();
假設您撰寫了一個泛型類別 :
- GenericFoo.java 代碼 :
- public class GenericFoo<T> {
- private T foo;
- public void setFoo(T foo) {
- this.foo = foo;
- }
- public T getFoo() {
- return foo;
- }
- }
- GenericFoo<ArrayList> foo1 = null;
- GenericFoo<LinkedList> foo2 = null;
- foo = new GenericFoo<List>();
- foo = new GenericFoo<Map>();
- GenericFoo<? extends List> foo = null;
- foo = new GenericFoo<LinkedList>();
- foo = new GenericFoo<ArrayList>();
- public void showFoo(GenericFoo foo) {
- // 針對List而制定的內容
- }
- public void showFoo(GenericFoo<? extends List> foo) {
- //
- }
- GenericFoo<String> foo = new GenericFoo<String>();
- foo.setFoo("caterpillar");
- GenericFoo<?> immutableFoo = foo;
- // 可以取得資訊
- System.out.println(immutableFoo.getFoo());
- // 可透過immutableFoo來移去foo所參考實例內的資訊
- immutableFoo.setFoo(null);
- // 不可透過immutableFoo來設定新的資訊給foo所參考的實例
- // 所以下面這行無法通過編譯
- //immutableFoo.setFoo("John");
事實上,GenericFoo<?> immutableFoo 相當於 GenericFoo immutableFoo. 除了可以向下限制,您也可以向上限制,只要使用 "super" 關鍵字,例如 :
- GenericFoo<? super StringBuilder> foo;
擴充泛型類別、實作泛型介面 :
您可以擴充一個泛型類別,保留其型態持有者,並新增自己的型態持有者,例如先寫一個父類別 :
- GenericFoo.java
- public class GenericFoo<T1, T2> {
- private T1 foo1;
- private T2 foo2;
- public void setFoo1(T1 foo1) {
- this.foo1 = foo1;
- }
- public T1 getFoo1() {
- return foo1;
- }
- public void setFoo2(T2 foo2) {
- this.foo2 = foo2;
- }
- public T2 getFoo2() {
- return foo2;
- }
- }
- SubGenericFoo.java
- public class SubGenericFoo<T1, T2, T3>
- extends GenericFoo<T1, T2> {
- private T3 foo3;
- public void setFoo3(T3 foo3) {
- this.foo3 = foo3;
- }
- public T3 getFoo3() {
- return foo3;
- }
- }
介面實作也是類似,例如先定義一個介面:
- IFoo.java
- public interface IFoo<T1, T2> {
- public void setFoo1(T1 foo1);
- public void setFoo2(T2 foo2);
- public T1 getFoo1();
- public T2 getFoo2();
- }
- GenericFoo.java
- public class GenericFoo<T1, T2> implements IFoo<T1, T2> {
- private T1 foo1;
- private T2 foo2;
- public void setFoo1(T1 foo1) {
- this.foo1 = foo1;
- }
- public T1 getFoo1() {
- return foo1;
- }
- public void setFoo2(T2 foo2) {
- this.foo2 = foo2;
- }
- public T2 getFoo2() {
- return foo2;
- }
- }
* Java Gossip: 型態通配字元
* Java Gossip: 擴充泛型類別、實作泛型介面
* Java 1.5 特性 : Generic Types
* JavaWorld@TW Java論壇- Generics 簡介
作者已經移除這則留言。
回覆刪除GenericFoo foo1 = new GenericFoo(); 你少了<boolean>,可能是你直接key <和>,但是你應該要轉成& lt ;和 & gt ;
回覆刪除You are right. Thanks for your feedback!
刪除非常感謝您的解說和無私的分享
回覆刪除You are welcome!
刪除The best explanation of Generic, I cannot understand Generic after reading so many books and articles. This is the best. I understand the concept immediately.
回覆刪除