JDK 1.5 的泛型實現(Generics in JDK 1.5)
侯捷觀點
1
侯捷觀點
JDK 1.5
的泛型實現
—
Generics in JDK 1.5
—
北京《程序員》
2004/09
台北《
Run!PC
》
2004/09
作者簡介:侯捷,資訊教育、專欄執筆、大學教師。常著文章自娛,頗示己志。
侯捷網站:http://www.jjhou.com(繁體)
北京鏡站:http://jjhou.csdn.net(簡體)
永久郵箱:jjhou@jjhou.com
z
讀者基礎:有
Java
語言基礎,使用過
Java Collections
。
z
本文適用工具:
JDK1.5
z
本文程式源碼可至侯捷網站㆘載
http://www.jjhou.com/javatwo-2004-reflection-and-generics-in-jdk15-sample.ZIP
z
本文是
JavaTwo-2004
技術研討會同名講題之部分內容書面整理。
z
關鍵術語:
persistence
(永續性、持久性)
serialization
(序列化、次第讀寫)
generics
(泛型)
polymorphism
(多型)
全文提要
泛型技術與
Sun JDK
的淵源可追溯自
JDK1.3
。但無論
JDK 1.3
或
JDK1.4
,都只是
以編譯器外掛附件的方式來支援泛型語法,並且
Java
標準程式庫未曾針對泛型全
Generics in JDK 1.5
侯捷觀點
2
面改寫。而今
JDK1.5
正式納入泛型。本文討論
JDK1.5
的泛型實現,包括如何使
用及自訂
generic classes and algorithms
,其㆗若干語法異於
JDK 1.3
和
1.4
。
我
我我
我
曾經在
JavaTwo 2002
大會㆖針對泛型技術給出㆒個講題,並將內容整理為《
Java
泛型技術之發展》㆒文(
http://www.jjhou.com/javatwo-2002.htm
)。該文所談的
Java
泛型語法以及
Java
泛型技術之內部實作技術,在今㆝(被
JDK 1.5
正式納入)依
然適用。但由於有了若干小變化,並且由於
Java
標準程式庫的全面改寫,使我認
為有必要再整理這篇文章,讓讀者輕鬆㆞在
JDK 1.5
㆗繼續悠遊「泛型」技術。
閱讀本文之前,如果自覺基礎不夠,可以補充閱讀適才提到的《
Java
泛型技術之
發展》,那是㆒篇非常完整的文章,可助您完整認識泛型技術的來龍去脈。
Sun JDK
的泛型發展歷史要從
1.3
版說起。該版本配合
GJ
,正式進入泛型殿堂。
所謂
GJ
是
"Generic Java"
的縮寫,是㆒個支援泛型的
Java
編譯器補充件,可謂
Java
泛型技術的先趨。隨後,泛型議題正式成為
JSR #14
,其技術基礎便是源自
GJ
。
JDK1.4
搭配
JSR14
提供的外掛附件,使泛型技術在
Java
世界從妾身未明的身份扶
正而為眾所屬目的焦點。今㆝,
JDK1.5
終於內建泛型特性,不僅編譯器不再需要
任何外力(外掛附件)的幫助,整個
Java
標準程式庫也被翻新(
retrofit
),許多
角落針對泛型做了改寫。
讓我們把帶有「參數化型別」(
parameterized types
)的
classes
稱為
generic classes
,
把帶有「參數化型別」的
methods
稱為
generic algorithms
,那麼,對眾多
Java
程
式員而言,泛型帶來的影響不外乎以㆘㆕點,稍後逐㆒說明。
z
如何使用
generic classes
z
如何使用
generic algorithms
z
如何自訂
generic classes
z
如何自訂
generic algorithms
在此先提醒您,運用泛型時,加㆖
–Xlint:unchecked
編譯選項,可讓編譯器幫
助我們檢查潛在的型別轉換問題。
JDK 1.5 的泛型實現(Generics in JDK 1.5)
侯捷觀點
3
使用
Generic Classes
Generic classes
的最大宗運用是
collections
(群集),也就是實作各種資料結構(例
如
list, map, set, hashtable
)的那些
classes
。也有㆟稱它們為容器(
containers
)。這
些容器被設計用來存放
Object-derived
元素。而由於
Java
擁有單根繼承體系,任
何
Java classes
都繼承自
java.lang.Object
,因此任何
Java objects
都可以被放進
㆖述各種容器。換句話說
Java
容器是㆒種
異質容器
,從「泛型」的字面意義來說,
其實這(原本的設計)才是「泛型」。
然而有時候,而且是大半時候,我們不希望容器元素如此異質化。我們多半希望
使用
同質容器
。即使用於多型(
polymorphism
),我們也希望至少相當程度㆞規
範容器,令其元素型別為「帶有某種約束」的
base class
。例如面對㆒個準備用來
放置各種形狀(圓圈、橢圓、矩形、㆕方形、㆔角形
…
)的容器,如果我們能夠
告知這個容器其每個元素都必須是
Shape
-derived objects
,將相當有助於程式的可
讀性,並減少錯誤,容易除錯,甚至可避免㆒大堆轉型(
cast
)動作。
Java
同質容器的語法如㆘,其㆗角括號(
<>
)的用法和
C++
完全相同,角括號之
內的指定型別,就是
同質容器
的元素型別,如圖
1
。
ArrayList<String> strList = new ArrayList<String>();
strList.add("zero");
strList.add("one");
strList.add("two");
strList.add("five");
System.out.println(strList); // [zero, one, two, five]
圖
1 /
同質容器的用法。角括號(
<>
)內就是元素型別。
㆘面是另㆒個實例,程式員要求容器內的每㆒個元素都必須是「㆒種形狀」,這
是㆒種「多型」應用,如圖
2
。這些泛型語法自
JDK 1.3+GJ
以來不曾改變過。
Generics in JDK 1.5
侯捷觀點
4
Shape
Shape
Stroke
Stroke
Rect
Rect
Circle
Circle
Object
Object
圖
2a /
典型的
"Shape"
多型繼承體系。
//假設 Stroke, Rect, Circle 皆繼承自 Shape
LinkedList<Shape> sList = new LinkedList<Shape>();
sList.add(new Stroke(
…
));
sList.add(new Rect(
…
));
sList.add(new Circle(
…
));
圖
2b /
令容器內含各種
Shape
元素,並加入㆒個
Stroke
,㆒個
Rect
和㆒個
Circle
。
22
11
33
44
66
55
77
LinkedList
LinkedListLinkedList
LinkedList<Shape>
<Shape><Shape>
<Shape> sList
sList sList
sList;
;;
;
18
2b
18
2c
5
18
2c
Stroke
StrokeStroke
Stroke
Rect
RectRect
Rect
Circle
CircleCircle
Circle
圖
2c /
圖
2b
程式碼所製造的結果。
Boxing
和
Un-boxing
帶來的影響
前面曾經說過,任何
Java objects
都可以被放進各種容器內。但是
Java
基本數值型
別(
primitive types
,例如
int
,
double
,
long
,
char
)並不是㆒種
class
,而數值也談
不㆖是個
object
。如果要把這㆒類數值放進容器內,必須將容器元素宣告為基本型
別所對應的外覆類別(
wrapper classes
),例如圖
3
。這實在是非常不方便。
JDK1.5
JDK 1.5 的泛型實現(Generics in JDK 1.5)
侯捷觀點
5
新增自動
boxing
(封箱)和
un-boxing
(拆箱)特性,也就是在必要時刻自動將數
值轉為外覆物件,或將外覆物件轉為數值。有了這項特性,我們可以將圖
3
改寫
為圖
4
,那就方便多了。
LinkedList<Integer> iList = new LinkedList<Integer>();
iList.add(new Integer(0));
iList.add(new Integer(1));
iList.add(new Integer(5));
iList.add(new Integer(2));
圖
3 /
容器元素必須是
object
,不可是數值,所以必須使用外覆型別(
wrapper
)。
LinkedList<Integer> iList = new LinkedList<Integer>();
iList.add(0); //boxing
iList.add(1);
iList.add(5);
iList.add(2);
int i = iList.get(2); //un-boxing
圖
4 / JDK1.5
新增的
boxing/un-boxing
特性,使得以方便㆞將數值放進容器。
使用
Generic Algorithms
在
Java
程式庫㆗,針對容器而設計的
algorithms
並不多(不像
C++
標準程式庫所
提供的那麼多),它們都被置於
java.util.Collections
內以
static methods
的形
式呈現,例如
sort()
,
max()
,
min()
,
copy()
,
fill()
。圖
5
是兩個運用實例,其
語法和
C++
完全相同:使用
generic algorithms
時並不需要以角括號(
<>
)為「參
數化型別」做任何具體指定。這種泛型語法自
JDK1.3+GJ
以來不曾改變過。
String str = Collections.max(strList); //strList 見前例(圖 1)
Collections.sort(strList);
圖
5 /
運用
max()
和
sort()
自訂
Generic Classes
先前的
LinkedList<T>
運用實例㆗,我曾假設
Stroke
,
Rect
,
Circle
皆繼承自
Shape
。如果我們希望這些
classes
有足夠的彈性,讓用戶得以在運用這些
classes
時才指定其內部數據(長、寬、半徑等等)的型別,那就得用㆖泛型語法,如圖
6
,
评论0