2006
年
8
月
14
日
星期一
管中窺虎
在學習
java 1.5
的過程中,我使用了
sun
公布的
tutorial
,這份文檔寫的比較詳盡易明,但是對于想快速了解
tiger
而且具有較好
java
基礎的人來說,大篇幅的英文文檔是比較耗時間和非必需的,所以我將會歸納這份文檔的主要內容,在保證理解的底線上,盡力減少閱讀者需要的時間。
?
在以下地址可以進入各新增語言特色介紹以及下載相關文檔(若有)。
http://java.sun.com/j2se/1.5.0/docs/relnotes/features.html
?
這一篇是接著上兩篇繼續的。
第一道虎紋:
generic
-泛型
/
類屬(三)
一些零碎
?
關于類:
?
?
List?
<
?String?
>
?l1?
=
?
new
?ArrayList?
<
?String?
>
?();?

List?
<
?Integer?
>
?l2?
=
?
new
?ArrayList?
<
?Integer?
>
?();?

System.out.println(l1.getClass()?
==
?l2.getClass());?
?
這樣的代碼打印出什么?腦子有沒有點混亂?事實是
true
,雖然類型參數不一樣,但它們在運行時終歸是同一個類。一個
class
可以有不同的
type
。由于靜態的變量和方法是被這個類的所有實例共享的,所以在靜態的方法,初始化塊,靜態變量的聲明或初始化中引用類型變量(前面的
T
這一類的東西)是非法的。
?
關于轉換和
instanceOf
:
?
正因為類的類型是被所有的實例共享的,所以去問一個實例是否為特定的類型是無意義的。
Collection?cs?
=
?
new
?ArrayList?
<
?String?
>
?();?


if
?(cs?
instanceof
?Collection?
<
?String?
>
?)?
{?
?}
?
//
?illegal?
這樣的代碼是非法的。
?
Collection?
<
?String?
>
?cstr?
=
?(Collection?
<
?String?
>
?)?cs;?
//
?unchecked?warning?
?
同樣的,上面這行代碼將會有
unchecked warning
,
因為它試圖做的類型檢查,是根本不會在運行時被執行的。同樣的,類型變量也是無效的:
?
<
?T?
>
?T?badCast(T?t,?Object?o)?
{?
return
?(T)?o;?
//
?unchecked?warning?
}
?
?
總而言之,類型變量在運行時是不存在的,意味著它們不會對運行表現增加任何時間上或者空間上的累贅,這樣挺好,但同時也意味著,別指望用它們來做類型轉換。
?
?
關于數組
?
一個數組對象的元素類型是不能為類型變量或者帶類型參數的類型,除非是一個非受限通配符類型,你可以聲明一個元素類型為類型變量或者帶類型參數的類型的數組類型,但是不能聲明這樣的數組對象。好吧,你舌頭打結了吧?說實話,在翻譯這段話前,我不僅舌頭打結,連神經都快打結了,即使如此,我還是不能肯定我翻譯的是否正確。原文如下:
The component type of an array object may not be a type variable or a parameterized
type, unless it is an (unbounded) wildcard type.You can declare array types whose
element type is a type variable or a parameterized type, but not array objects.
通過閱讀下面的代碼,也許幫助你理解這團亂麻,之所以有上面這樣的復雜規定,是為了避免這樣的情形:
?
List?
<
?String?
>
?[]?lsa?
=
?
new
?List?
<
?String?
>
?[
10
];?
//
?實際上不被允許,現在只是假設可以這樣寫?
Object?o?
=
?lsa;?

Object[]?oa?
=
?(Object[])?o;?

List?
<
?Integer?
>
?li?
=
?
new
?ArrayList?
<
?Integer?
>
?();?

li.add(
new
?Integer(
3
));?

oa[
1
]?
=
?li;?
//
?有錯,但通過了運行時的檢查?
String?s?
=
?lsa[
1
].get(
0
);?
//
?run-time?error?-?ClassCastException?
?
如果運行了數組元素為帶類型變量的類型,就會出現這樣的情況,明明通過了編譯,沒有任何
unchecked warning
,但在運行時卻出錯。所以泛型必須設計為這個樣子,以保證:如果你的整個應用程序在
jdk1.5
下通過了編譯而且沒有
unchecked warning
,那它就是類型安全的。
然而,你還可以用通配符數組,接下來是以上代碼的兩個改版。
?
第一個讓數組類和數組對象都用了通配符,在最后一句,如果要賦值給
String
,就必須用顯示的類型轉換,雖然出了錯,那么可以說這不是機制的錯誤了,而是程序員的錯。
?
List?
<
?
?
?
>
?[]?lsa?
=
?
new
?List?
<
?
?
?
>
?[
10
];?
//
?ok,?array?of?unbounded?wildcard?type?
Object?o?
=
?lsa;?

Object[]?oa?
=
?(Object[])?o;?

List?
<
?Integer?
>
?li?
=
?
new
?ArrayList?
<
?Integer?
>
?();?

li.add(
new
?Integer(
3
));?

oa[
1
]?
=
?li;?
//
?correct?
String?s?
=
?(String)?lsa[
1
].get(
0
);?
//
?run?time?error,?but?cast?is?explicit?
?
?
?
在第二個版本里,我們定義了帶類型參數的類型的數組類,但數組對象用了通配符,這樣是合法的,但不安全,產生了
unchecked warning
,而且最終也確實出錯了。但至少,我們得到了警告。
?
List?
<
?String?
>
?[]?lsa?
=
?
new
?List?
<
?
?
?
>
?[
10
];?
//
?unchecked?warning?-?this?is?unsafe!?
Object?o?
=
?lsa;?

Object[]?oa?
=
?(Object[])?o;?

List?
<
?Integer?
>
?li?
=
?
new
?ArrayList?
<
?Integer?
>
?();?

li.add(
new
?Integer(
3
));?

oa[
1
]?
=
?li;?
//
?correct?
String?s?
=
?lsa[
1
].get(
0
);?
//
?run?time?error,?but?we?were?warned?
?
?
?
同樣的,試圖創造一個元素類型是類型變量的數組對象是通過不了編譯的:
?
這些錯誤的原因歸結起來也是我們之前討論過的了,就是因為運行時,這些類型變量是不存在的,無法決定數組的實際類型。
要突破這些限制,可以使用
類名稱字面常量(
class literal
)作為運行時類型標記,請看下文。
?
?
以類名稱字面常量作為運行時類型標記
Jdk1.5
里,
java.lang.Class
是泛型的,這就有趣了,
Class
類有個類型參數
T
,那
T
代表什么?
T
代表了
Class
類的對象所代表的類型,又來繞口令了不是?
舉例吧:
String.class
的類型就是
Class<String>
,
Serializable.class
的類型就是
Class
<
Serializable
>
。現在
Class
類的
newInstance()
方法返回一個
T
,你現在在創造對象的時候獲得的類型更加精確了。(
1.4
里是固定地返回
Object
)
假設你要寫一個工具方法,從數據庫里執行一個
SQL
查詢,將符合查詢的結果以對象集合返回,有一鐘方法就是顯示的使用一個工廠對象,如下:
interface
?Factory?
<
?T?
>
?
{?T?make();?}
?


public
?
<
?T?
>
?Collection?
<
?T?
>
?select(Factory?
<
?T?
>
?factory,?String?statement)?
{?

???????Collection?
<
?T?
>
?result?
=
?
new
?ArrayList?
<
?T?
>
?();?


/**/
/*
?run?sql?query?using?jdbc?
*/
?


?????
for
?(
/**/
/*
?iterate?over?jdbc?results?
*/
?)?
{?

??????T?item?
=
?factory.make();?


/**/
/*
?use?reflection?and?set?all?of?item’s?fields?from?sql?results?
*/
?

??????result.add(item);?

??????}
?

??????
return
?result;?

}
?

你可以這樣調用它:
?
select(
new
?Factory?
<
?EmpInfo?
>
?()?{?
public
?EmpInfo?make()?{?
???return
?
new
?EmpInfo();?
}?
}?,?”selection?string”);?
?
或者聲明一個實現
Factory
接口的類
EmpInfoFactory
,
class
?EmpInfoFactory?
implements
?Factory?
<
?EmpInfo?
>
?
?{?
public
?EmpInfo?make()?{?
??
return
?
new
?EmpInfo();?
}?
}?
然后這樣調用它:
select(getMyEmpInfoFactory(),?”selection?string”);?

?
這兩個辦法的缺點就是,你要么在調用處寫上羅嗦的工廠類,要么為每個類都寫一個工廠類,在每個調用的地方傳入一個工廠對象,這看起來也不怎么自然。
用
類名稱字面常量來作為一個工廠對象就很自然,它可以用于反射機制,如果不用泛型,可以這樣寫:
C...ollection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”);
public
?
static
?Collection?select(Class?c,?String?sqlStatement)?{?
???Collection?result?
=
?
new
?ArrayList();?
???
/*
?run?sql?query?using?jdbc?
*/
?
???
for
?(?
/*
?iterate?over?jdbc?results?
*/
?)?{?
???????Object?item?
=
?c.newInstance();?
??????
/*
?use?reflection?and?set?all?of?item’s?fields?from?sql?results?
*/
?
??????result.add(item);?
???}?
return
?result;?
}?
?
然而這樣我們得不到我們想要的包含確切的類型的對象集,而現在
Class
是泛型的了,我們可以用它來達到目的:
Collection?
<
?EmpInfo?
>
?emps?
=
?
?sqlUtility.select(EmpInfo.
class
,?”select?
*
?from?emps”);?
public
?
static
?
<
?T?
>
?Collection?
<
?T?
>
?select(Class?
<
?T?
>
?c,?String?sqlStatement)?{?Collection?
<
?T?
>
?result?
=
?
new
?ArrayList?
<
?T?
>
();?
???
/*
?run?sql?query?using?jdbc?
*/
?
???
for
?(?
/*
?iterate?over?jdbc?results?
*/
?)?{?T?item?
=
?c.newInstance();?
??????
/*
?use?reflection?and?set?all?of?item’s?fields?from?sql?results?
*/
?
??????result.add(item);?
???}?
return
?result;?
}?
精確的類型,類型安全。齊活兒了。
?
這一篇到此為止,泛型的部分還剩下比較復雜的兩個部分,經過考慮后決定延后完成,先完成
tiger
的其他幾個簡易的新特色會比較有趣些。
posted on 2006-08-14 17:08
Ye Yiliang 閱讀(2006)
評論(0) 編輯 收藏 所屬分類:
Java