Generic(泛型) 的基本用法
這個功能是在 J2SE 5.0 中所提供的,目的是要協助程式開發者建立安全的 Generic Class;當然在沒有 Generic Class 之前,可透過 Object Class 並搭配 polymorphism 的功能達成類似的功能。
以下直接用程式範例來說明,可以比較兩者使用方式上的不同:
Object Class + Polymorphism 機制
ObjectFoo.java
ObjectFooDemo.java//使用 polymorphism 的方式定義各型態可用的 class
public class ObjectFoo {
private Object foo;
public void setFoo(Object foo) {
this.foo = foo;
}
public Object getFoo() {
return foo;
}
}
public class ObjectFooDemo {
public static void main(String[] args) {
ObjectFoo foo1 = new ObjectFoo();
ObjectFoo foo2 = new ObjectFoo();
foo1.setFoo(new Boolean(true));
Boolean b = (Boolean)foo1.getFoo(); //此處傳回 Object,因此必須轉換型態
foo2.setFoo(new Integer(10));
Integer i = (Integer)foo2.getFoo(); //此處傳回 Object,因此必須轉換型態
}
}
Generics (泛型)
【注意】在"型態"的部分,都是以「<T>」來代替。
GenericFoo.java
GenericFooDemo.javapublic class GenericFoo<T> {
private T foo;
public void setFoo(T foo) {
this.foo = foo;
}
public T getFoo() {
return foo;
}
}
public class GenericFooDemo {
public static void main(String[] args) {
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(); //不需要再轉換型態
}
}
當然還有其他的用法,例如同時使用多種不同型態時:
或是與陣列的搭配使用:public class GenericFoo2<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;
}
}
甚至在 Generic Class 中再內含一個 Generic Classpublic class GenericFoo3<T> {
private T[] fooArray;
public void setFooArray(T[] fooArray) {
this.fooArray = fooArray;
}
public T[] getFooArray() {
return fooArray;
}
}
public class WrapperFoo<T> {
//之前宣告的 Generic Class
private GenericFoo<T> foo;
public void setFoo(GenericFoo<T> foo) {
this.foo = foo;
}
public GenericFoo<T> getFoo() {
return foo;
}
}
Generic(泛型) 的進階用法
以上所介紹的都是 Generics 的基本用法,然而 Generics 的用法不僅如此,以下介紹三種進階的用法:
限制 Generic 可用型態
定義 Generic Class 時,預設可以用任何型態來 instantiate generic class;但其實是可以限制在特定的型態的,以下用個範例來說明:
如上述,透過 ListGenericFoo 所產生的 object,僅能使用 List 或其衍生出來的型態,使用其他則在編譯時會發生錯誤。import java.util.List;
//表示僅能使用由 List 或其衍生出來的型態
//用其他非 List 衍生出來的型態,compile 時會發生錯誤
public class ListGenericFoo<T extends List> {
private T[] fooArray;
public void setFooArray(T[] fooArray) {
this.fooArray = fooArray;
}
public T[] getFooArray() {
return fooArray;
}
}
【註】由此可知,其實「<T>」與「<T extends Object>」是相同的
Wildcard
假設有個 GenericFoo class,使用下列方式宣告:
GenericFoo<Integer> foo1 = null;如此一來,foo1 就只能參考到由 GenericFoo<Interger> 所產生出來的物件。
GenericFoo<Boolean> foo2 = null;
但若希望達成下面的功能,應該要如何作呢?
foo = new GenericFoo<ArrayList>();答案就是使用 Wildcard,以下用範例程式來說明:
foo = new GenericFoo<LinkedList>();
GenericWildcard.java
GenericWildcardDemo.javapublic class GenericWildcard<T> {
private T foo;
public void setFoo(T foo) {
this.foo = foo;
}
public T getFoo() {
return foo;
}
}
import java.util.*;
public class GenericWildcardDemo {
public static void main(String[] args) {
GenericWildcard<? extends List> foo = null;
//成功參考到 ArrayList 型態
foo = new GenericWildcard<ArrayList>();
System.out.println(foo.toString());
//成功參考到 LinkedList 型態
foo = new GenericWildcard<LinkedList>();
System.out.println(foo.toString());
//產生編譯錯誤,String不屬於 List 擴充出來的型態
foo = new GenericWildcard<String>();
}
}
Extends Generic Class
最後,還可以將原先定義好的 Generic Class 進行擴充,不過在擴充時建議保留 parent class 的型態持有者,也就是範例程式中的 T1、T2:
GenericFoo4.java
SubGenericFoo4.javapublic class GenericFoo4<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;
}
}
public class SubGenericFoo4<T1, T2, T3> extends GenericFoo4<T1, T2> {
private T3 foo3;
public void setFoo3(T3 foo3) {
this.foo3 = foo3;
}
public T3 getFoo3() {
return foo3;
}
}
因此可以了解,Generics 在使用上的彈性是相當大的,透過 Generics 的機制,讓原本需要透過 polymorphism 才能達成的功能就輕鬆達成了! 而 compiler 還可以協助檢查錯誤! 如果使用得宜,的確是一個相當好用的功能。
沒有留言:
張貼留言