文/陳剛 2005-11-09?
一、前言
泛型這個詞在現在的JAVA挺時髦,光從字面上你是無法知道它代表些什么東東的,所以我們還是不要從字面去理解,而是從一些實例去了解它吧。
二、泛型之前的日子
JDK1.4之前是沒有泛型的概念的,所以我們才會有下面的代碼:
List list = new ArrayList();
list.add("aaaa");
list.add("bbbb");
list.add("cccc");
for (Iterator it = list.iterator(); it.hasNext();) {
String str = (String) it.next();
System.out.println(str);
}
上面是一段很平常的代碼,在一個List集合加入一些字符串,然后再用一個遍歷循環把它打印出來。“String str = (String) it.next()”這一句我們可以看到List取出值都是Object,所以我們要得String型,還要做一個類型轉換,真是麻煩。更麻煩的是list.add(Object obj)的參數是Object類型,所以如果我們一不小心把list.add("cccc");寫成list.add(new Integer(76));程序在循環打印的類型轉換中就會出錯。
問題:我們能不能讓add方法只認String型呢?
回答:可以!用JDK5.0的泛型。
三、泛型后的幸福生活
JAVA有了泛型后,就象十年的老光棍討了老婆,那個好處自不待言。我們來看看上面的例子改成泛型的寫法是怎么樣的:
List<String> list = new ArrayList<String>();
list.add("aaaa");
list.add("bbbb");
list.add("cccc");
for (Iterator<String> it = list.iterator(); it.hasNext();) {
String str=it.next();
System.out.println(str);
}
看到差別了嗎?泛型其實很簡單,就是在定義類型的后面加上"<類型>"這樣子的聲明就行了,它主要還有以下差別:
- list.add方法只能接受String類型。list.add(new Integer(76))這樣的語句不需要運行程序,在編譯時就會檢查通不過。
- it.next()的返回值不再是Object,而變成了String
當然我們其實在循環部份也可以象下面這么寫,是不是簡潔了很多呢 :-)
List<String> list = new ArrayList<String>();
list.add("aaaa");
list.add("bbbb");
list.add("cccc");
for (String str : list) {
System.out.println(str);
}
當然需要說明的是,List不僅可以List<String>,也可以是List<Integer>等等其他任何類型。
四、更深入了解泛型
(1)層層推進的泛型聲明
“List<List> list;”表示什么呢?就是只接收List型的參數,比如:
??????? List<List> list = new ArrayList<List>();
list.add(new ArrayList());
list.add(new Vector());
list.add(new LinkedList());
這里要注意List是接口,ArrayList、Vector、LinkedList都是這一接口下的實現類。下面這個有點怪異了,“List<List<String>> list;”表示它只接受List型的參數,而且這種List型的參數又是只是只接受String型,有點層層推進的味道在里面了。
??????? List<List<String>> list = new ArrayList<List<String>>();
list.add(new ArrayList<String>());
list.add(new Vector<String>());
list.add(new LinkedList<String>());
(2)使用泛型上限通通配符:extends
這里要著重強調一點:變量的泛型聲明和方法的參數的泛型聲明有很大差別。
變量聲明成某類型,同時也可以接受它的子類。比如說Integer、Long、Float都是抽象類Number的子類,所以下面的代碼一點問題也沒有:
??????? List<Number> list = new ArrayList<Number>();
list.add(new Integer(1));
list.add(new Long(1));
list.add(new Float(1.2));
但如果換成方法參數的泛型聲明則要嚴格得多了:子類也是不行的。比如下面的代碼就是錯誤的,因為printList參數只接受Number值的List,就是是Number子類的Integer值的List也不行。
??? public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(new Integer(2));
printList(list);
}
private static void printList(List<Number> list){
for (Number num : list) {
System.out.println(num);
}
}
上面代碼修改的方法有兩個,如下
修改方法一:改變量的泛型聲明
將 List<Integer> list = new ArrayList<Integer>();
改為 List<Number> list = new ArrayList<Number>();
修改方法二:用界限通配符改方法參數的泛型聲明
將 printList(List<Number> list)
改為 printList(List<? extends Number> list)
說明:extends 的含義就是表示參數可以接受Number型的子類。
(3)使用泛型下限通通配符:super
?? 在上限就有下限,下限行就是super,用法和extends一樣,含義則和extends相反。比如printList(List<? super Integer> list)表示參數可以接受Integer型及Integer型的超類,即Number了,當然也包括Object這個頂級類。
(4)配置符:?
?表示可以接受任何類型,不過我覺得它用得不多,因為printList(List<?> list)和printList(List list)的作用是一樣的。
五、創建一個支持泛型的類
(1)創建一個泛型的類
public class Point<T> {
T x;
T y;
??? public T getX() {
return x;
}
??? public T getY() {
return y;
}
??? public void setX(T x) {
this.x = x;
}
??? public void setY(T y) {
this.y = y;
}
}
使用這個類的代碼如下:
??????? Point<Integer> p = new Point<Integer>();
p.setX(new Integer(1));
p.setY(new Integer(2));
Point<String> b = new Point<String>();
b.setX("1");
b.setY("2");
說明:在Point<T>的定義中,T并非關鍵字,你也可以這樣定義Point<ABC>,當然一般還是寫T吧,簡單也規范。
(2)泛型類的繼承與實現
java.util.Comparator類是JDK里用來排序的,其源代碼如下:
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
?? 一個實現此接口的類如下:
??? public class MyComparator<T> implements Comparator<ObjectInstance> {
public int compare(ObjectInstance o1, ObjectInstance o2) {
String s1 = o1.getObjectName().getCanonicalName();
String s2 = o2.getObjectName().getCanonicalName();
return s1.compareToIgnoreCase(s2);
}
}
說明:ObjectInstance可能大家還太明白,這是我實際項目中的一段代碼(關于JMX的),ObjectInstance全稱javax.management.ObjectInstance。MyComparator的使用代碼如下:
List<ObjectInstance> mbeans = new ArrayList<ObjectInstance>(set);
Collections.sort(mbeans, new MyComparator<ObjectInstance>());
六、最后的感言
JAVA有了泛型就象老光棍討了老婆,好處大大的,但和女人一樣麻煩也跟著來了:它的嚴格類型檢查,使隱藏的BUG更少。有些地方確實也使代碼簡潔了,有些地方卻會使得代碼更復雜。所以運用之妙在于是否用得適當,盡量把泛型往簡單里用,別越搞越復雜了。?
參考資料
J2SE 5.0中的泛型 http://www.matrix.org.cn/resource/article/43/43634_java_generics.html
作者簡介
陳剛,廣西桂林人,著作有《Eclipse從入門到精通》
您可以通過其博客了解更多信息和文章:http://www.ChenGang.com.cn