管中窺虎
在學(xué)習(xí)
java 1.5
的過程中,我使用了
sun
公布的
tutorial
,這份文檔寫的比較詳盡易明,但是對(duì)于想快速了解
tiger
而且具有較好
java
基礎(chǔ)的人來說,大篇幅的英文文檔是比較耗時(shí)間和非必需的,所以我將會(huì)歸納這份文檔的主要內(nèi)容,在保證理解的底線上,盡力減少閱讀者需要的時(shí)間。
?
在以下地址可以進(jìn)入各新增語(yǔ)言特色介紹以及下載相關(guān)文檔(若有)。
http://java.sun.com/j2se/1.5.0/docs/relnotes/features.html
?
第一道虎紋:
generic
-泛型
/
類屬
什么是泛型
泛型讓你在類這一層次上進(jìn)行抽象??纯蠢樱?br />
List?myIntList?
=
?
new
?LinkedList();?
//
?1?
myIntList.add(
new
?Integer(
0
));?
//
?2?
Integer?x?
=
?(Integer)?myIntList.iterator().next();?
//
?3?
?
?
第
3
句的轉(zhuǎn)換類型有點(diǎn)麻煩吧~?編譯器只能保證容器類里放的是
Object
對(duì)象,要使用他們只能這樣去轉(zhuǎn)換。而且這樣的轉(zhuǎn)換也并不是完全安全的,程序員可能犯錯(cuò)誤,容器里的對(duì)象未必是他以為的對(duì)象。有沒有辦法顯式地表達(dá)出程序員的意圖,將該容器限制為只能保存特定類型的對(duì)象?這正是
generic
-泛型的核心用意。
?
?
List?
<
?Integer?
>
?myIntList?
=
?
new
?LinkedList?
<
?Integer?
>
?();?
//
?1’?
myIntList.add(
new
?Integer(
0
));?
//
2’?
Integer?x?
=
?myIntList.iterator().next();?
//
?3’?
?
?
這樣子我們就聲明了一個(gè)只放
Integer
的
List
,我們說
List
是一個(gè)
generic Interface
,接收了一個(gè)類型參數(shù),在上例中就是
Integer
。在初始化的時(shí)候,同樣的也指定了這個(gè)類型參數(shù)。
要注意這些工作的效果不是僅僅把原來的第
3
句的轉(zhuǎn)換工作省掉,而是由此讓編譯器確保了這個(gè)
List
在程序的任何位置任何時(shí)候都用以存放正確的類型,而原來的類型轉(zhuǎn)換僅僅告訴我們?cè)谶@一單點(diǎn)處程序員自己認(rèn)為的類型。
泛型由此為程序,尤其是大型程序,帶來了可讀性和健壯性。
定義簡(jiǎn)單的泛型
?
尖括號(hào)內(nèi)的標(biāo)識(shí)符就是一個(gè)類型形式參數(shù)。類似于方法的參數(shù),當(dāng)你使用的時(shí)候就替換一個(gè)實(shí)際參數(shù)進(jìn)去,只不過這個(gè)參數(shù)是個(gè)類型。在上面的例子中,我們就替換了一個(gè)
Integer
類型進(jìn)去。
在這里稍微說一下命名的規(guī)范,定義泛型中的形式參數(shù)時(shí),使用簡(jiǎn)潔有力又具有啟發(fā)性的名字,如果可以的話用單個(gè)字母更好。避免使用小寫,以免和普通的方法參數(shù)混淆。
?
泛型與子類
?
看看以下的例子語(yǔ)句合法嗎?
?
List?
<
?String?
>
?ls?
=
?
new
?ArrayList?
<
?String?
>
?();?
//
1?
List?
<
?Object?
>
?lo?
=
?ls;?
//
2?
?
第
2
句是行不通的,看看以下的語(yǔ)句:
lo.add(
new
?Object());?
//
?3?
String?s?
=
?ls.get(
0
);?
//
?4:?試圖將?Object?對(duì)象賦值給字符串對(duì)象,編譯錯(cuò)誤?
簡(jiǎn)而言之就是,原有類型的繼承關(guān)系是不會(huì)反映到對(duì)應(yīng)的泛型上來,在上述情況下,任何兩個(gè)泛型類型都不存在繼承關(guān)系。那么,習(xí)慣了面向接口編程的我們?cè)鯓尤ミm應(yīng)這種嚴(yán)格的使用限制呢?
?
通配符
假如我們要用一個(gè)方法把一個(gè)容器內(nèi)的元素都
print
出來,可以用這樣的代碼來實(shí)現(xiàn)
:
void
?printCollection(Collection?c)?
{?

?????????Iterator?i?
=
?c.iterator();?


????for?(k?=?0;?k?<?c.size();?k++)?
{?

?????????System.out.println(i.next());?

??????}
?????}
?
如果我們用新的泛型和新的
for
語(yǔ)句(你可以先不了解它,以后會(huì)談到)來嘗試同樣的功能,以下代碼可行嗎?
?
事實(shí)是,新的代碼的使用范圍非常有限,因?yàn)?/span>
Collection
<
Object
>
就只是一個(gè)放
Object
的容器泛類,它不是
任何其他
Collection
泛類的父類!真正擔(dān)任這個(gè)角色的是:
Collection
<
?
>
這個(gè)問號(hào)代表了未知,
Collection
<
?
>
的元素可以是任何類型,這就是通配類型。
現(xiàn)在我們可以這樣寫:
void
?printCollection(Collection?
<
?
?
?
>
?c)?
{?


??
for
?(Object?e?:?c)?
{?

?????????System.out.println(e);?

??????}
?
}
?
?
注意在循環(huán)里面,可以把元素賦值給一個(gè)
Object
類型,因?yàn)闊o(wú)論
c
里放的是什么,它肯定是一個(gè)
Object
,但向
c
里放置對(duì)象則是不安全的,因?yàn)椴恢?/span>
c
的泛型是什么。如下面這樣是不行的:
Collection?
<
?
?
?
>
?c?
=
?
new
?ArrayList?
<
?String?
>
?();?

c.add(
new
?Object());?
//
?編譯錯(cuò)誤?
?
add()
方面接納的是泛型的形式參數(shù)里描述的類型(或它的子類,希望你對(duì)此不感到混亂,呵呵。),然而此時(shí)我們只看到一個(gè)問號(hào),我們不知道它的類型參數(shù)是什么,當(dāng)然就不能放置對(duì)象進(jìn)去。唯一一個(gè)例外是
null
,它是任何一個(gè)類型的對(duì)象集的一分子。
?
受限通配符
省去一些說明性的代碼,以我們熟悉的幾何圖形家族例子來說明:
?
public
?
void
?drawAll(List?
<
?
?
?
extends
?Shape?
>
?shapes)?
{?
?}
?
?
一個(gè)
List<T>
,如果
T
是
Shape
的子類,那么這個(gè)
List
都可以被上面這個(gè)方法接納為參數(shù),這個(gè)就是受限的通配符。
Shape
就稱為這個(gè)通配符的上限。
看看下面的代碼,怎樣?
public
?
void
?addRectangle(List?
<
?
?
?
extends
?Shape?
>
?shapes)?
{?

??????shapes.add(
0
,?
new
?Rectangle());?
//
?compile-time?error!?
}
?
如上的方法依然是不行的,因?yàn)槲覀冎恢?/p>
shapes
是
Shape
或者其子類型的容器,然而具體類型是什么不知道,它未必是
Rectangle
的父類,所以不能放置元素進(jìn)去。
?
總結(jié)起來,我們要了解的事情有:
l????????
泛型的用意
l????????
泛型的幾種形式(普通,通配符,受限通配符)
l????????
泛型與繼承關(guān)系一起使用時(shí)的易錯(cuò)傾向,尤其是向泛型容器添加元素的情況。