wintysのブログ
BlogJava
聯系
管理
隨筆 - 117 文章 - 72 trackbacks - 0
聲明:原創作品(標有[原]字樣)轉載時請注明出處,謝謝。
常用鏈接
常用設置
常用軟件
常用命令
訂閱
留言簿
(7)
給我留言
查看公開留言
查看私人留言
隨筆分類
(130)
.NET(1)
Algorithm(4)
C++(6)
Database(3)
Error(18)
HelloWorld(1)
Hibernate(13)
Java(17)
JSP(8)
Linux(4)
Miscellaneous(2)
Note(2)
OTHER(2)
Pattern(25)
Skill(3)
Struts(10)
Tech(2)
Test(2)
Web(4)
Winty(3)
隨筆檔案
(123)
2013年1月 (1)
2012年2月 (2)
2010年6月 (1)
2010年4月 (3)
2010年3月 (4)
2010年1月 (3)
2009年10月 (4)
2009年9月 (2)
2009年8月 (4)
2009年7月 (12)
2009年6月 (32)
2009年5月 (20)
2009年4月 (10)
2009年3月 (24)
2008年12月 (1)
搜索
積分與排名
積分 - 155671
排名 - 391
最新評論
1.?re: [原]Hibernate一對多(單向)
太感謝了 非常仔細
--87
2.?re: [原]向MySQL數據庫插入Blob數據的問題
贊一個!
--moyue
3.?re: [原]Hibernate一對多(單向)
很帥氣~
--11
4.?re: [原]裝飾模式2-發票系統
評論內容較長,點擊標題查看
--dohkoos
5.?re: [原]開發自己的Windows Live Writer插件
評論內容較長,點擊標題查看
--天堂露珠
[導入]Java范型淺析
(轉載自:[url]http://blog.csdn.net/andycpp/archive/2007/08/17/1748731.aspx[/url])
寫的很好,保存一下.
從jdk1.5開始,Java中開始支持范型了。范型是一個很有用的編程工具,給我們帶來了極大的靈活性。在看了《java核心編程》之后,我小有收獲,寫出來與大家分享。
所謂范型,我的感覺就是,不用考慮對象的具體類型,就可以對對象進行一定的操作,對任何對象都能進行同樣的操作。這就是靈活性之所在。但是,正是因為沒有 考慮對象的具體類型,因此一般情況下不可以使用對象自帶的接口函數,因為不同的對象所攜帶的接口函數不一樣,你使用了對象A的接口函數,萬一別人將一個對 象B傳給范型,那么程序就會出現錯誤,這就是范型的局限性。所以說,范型的最佳用途,就是用于實現容器類,實現一個通用的容器。該容器可以存儲對象,也可 以取出對象,而不用考慮對象的具體類型。因此,在學習范型的時候,一定要了解這一點,你不能指望范型是萬能的,要充分考慮到范型的局限性。下面我們來探討 一下范型的原理以及高級應用。首先給出一個范型類:
public
class
Pair
<
T
>
...
{
public
Pair()
...
{ first
=
null
; second
=
null
; }
public
Pair(T first, T second)
...
{
this
.first
=
first;
this
.second
=
second; }
public
T getFirst()
...
{
return
first; }
public
T getSecond()
...
{
return
second; }
public
void
setFirst(T newValue)
...
{ first
=
newValue; }
public
void
setSecond(T newValue)
...
{ second
=
newValue; }
private
T first;
private
T second;
}
我們看到,上述Pair類是一個容器類(我會多次強調,范型天生就是為了容器類的方便實現),容納了2個數據,但這2個數據類型是不確定的,用范型T來表示。關于范型類如何使用,那是最基本的內容,在此就不討論了。
下面我們來討論一下Java中范型類的實現原理。在java中,范型是在編譯器中實現的,而不是在虛擬機中實現的,虛擬機對范型一無所知。因此,編譯器一 定要把范型類修改為普通類,才能夠在虛擬機中執行。在java中,這種技術稱之為“擦除”,也就是用Object類型替換范型。上述代碼經過擦除后就變成 如下形式:
public
class
Pair
...
{
public
Pair(Object first, Object second)
...
{
this
.first
=
first;
this
.second
=
second;
}
public
Object getFirst()
...
{
return
first; }
public
Object getSecond()
...
{
return
second; }
public
void
setFirst(Object newValue)
...
{ first
=
newValue; }
public
void
setSecond(Object newValue)
...
{ second
=
newValue; }
private
Object first;
private
Object second;
}
大家可以看到,這是一個普通類,所有的范型都被替換為Object類型,他被稱之為
原生類
。每當你用一個具體類去實例化該范型時,編譯器都會在原生類的基礎上,通過
強制約束
和
在需要的地方添加強制轉換代碼
來滿足需求,但是不會生成更多的具體的類(這一點和c++完全不同)。我們來舉例說明這一點:
Pair
<
Employee
>
buddies
=
new
Pair
<
Employee
>
();
//
在上述原生代碼中,此處參數類型是Object,理論上可以接納各種類型,但編譯器通過
強制約束
//
你只能在此使用Employee(及子類)類型的參數,其他類型編譯器一律報錯
buddies.setFirst(
new
Employee(
"
張三
"
));
//
在上述原生代碼中,getFirst()的返回值是一個Object類型,是不可以直接賦給類型為Employee的buddy的
//
但編譯器在此做了手腳,
添加了強制轉化代碼
,實際代碼應該是Employee buddy = (Employee)buddies.getFirst();
//
這樣就合法了。但編譯器做過手腳的代碼你是看不到的,他是以字節碼的形式完成的。
Employee buddy
=
buddies.getFirst();
下面我們再來考察一個更復雜的情況,如果我們的Pair類要保證第二個屬性一定要大于第一個屬性,該如何做?這就涉及到兩個屬性的比較,但是這2個屬性類 型未知,可以比較嗎?我們前面也講過,一般情況下不要涉及類型的具體信息。但是現在要比較2個屬性,不得不涉及類型的具體信息了。Java還是考慮到了這 一點,那就是,范型類可以繼承自某一個父類,或者實現某個接口,或者同時繼承父類并且實現接口。這樣的話,就可以對類型調用父類或接口中定義的方法了。代 碼如下:
public
class
Pair
<
T extends Comparable
>
...
{
public
boolean setSecond(T newValue)
...
{
boolean flag
=
false
;
If(newValue.compareTo(first)
>
0
)
...
{
second
=
newValue;
flag
=
true
;
}
return
flag;
}
private
T first;
private
T second;
}
我們看到,上面的范型T被我們添加了一個約束條件,那就是他必須實現Comparable接口,這樣的話,我們就可以對范型T使用接口中定義的方法了,也 就可以實現2個元素大小的比較。有人可能要問了,實現一個接口不是用implements嗎?上面怎么用extends呢??為了簡化范型的設計,無論是 繼承類還是實現接口,一律使用extends關鍵字。這是規定,沒辦法,記住就行了。若同時添加多個約束,各個約束之間用“&”分隔,比 如:public class Pair<T extends Comparable & Serializable>。那么編譯器是如何處理這種情況呢?前面講過,范型類最終都會被轉化為原生類。在前面沒有添加約束的時候,編譯器將范型 通通替換為Object;而增加了約束之后,通通用第一個約束來替換范型(上面的代碼就會用
Comparable來替換所有范型
),當需要用到其他約束中定義的方法的時候,通過插入強制轉化代碼來實現。在此就不給出具體的例子了。
下面我們來看看最后一個知識點,
定義一個函數,該函數接受一個范型類作為參數
。首先讓我們來看一個最簡單的情況,參數是一個實例化的范型類:
public
static
void
test(ArrayList
<
Number
>
l)
...
{
l.add(
new
Integer(
2
));
}
上述代碼中,形參list的元素被實例化為Number類型。在使用該函數的時候我們能不能傳入一個元素為Integer的list呢?看看下面代碼合法嗎?
ArrayList
<
Integer
>
l
=
new
ArrayList
<
Integer
>
();
test(l);
//
此處編譯器會報錯!!
答案上面已經給出了:不行!對于這種形參,實參的類型必須和他完全一致,即也應該是一個元素為Number的list才可以,其他的實參一律不行。這是為 什么呢?Integer不是Number的子類嗎?子類的對象傳遞給父類的引用,不可以嗎?這里我們就要注意了,
Integer確實是Number的子類,但是,ArrayList<Integer>并不是ArrayList<Number>的子類
,二者之間沒有任何的繼承關系!!因此這樣傳遞參數是不允許的。如果允許的話,會出現什么問題嗎?當然會,我們對test函數重新定義一下:
public
static
void
test(ArrayList
<
Number
>
l)
...
{
l.add(
new
Float(
2
));
}
大家可以看到,在函數內部,我們把Float類型的元素插入到鏈表中。因為鏈表是Number類型,這條語句沒問題。但是,如果實參是一個Integer 類型的鏈表,他能存儲Float類型的數據嗎??顯然不能,這樣就會造成運行時錯誤。于是,編譯器干脆就不允許進行這樣的傳遞。
通過分析我們看到,出錯的可能性只有一個:在向容器類
添加
內 容的時候可能造成類型不匹配。那么有些人可能會有這種要求:“我保證一定不對容器添加內容,我非常希望能夠將一個Integer類(Number類的子 類)組成的鏈表傳遞進來”。Sun的那幫大牛們當然會考慮到這種訴求,這樣的功能是可以實現的,并且還有兩種方式呢,看下面代碼:
//
1.在定義方法的時候使用
Wildcard
(也就是下述代碼中的問號)。
public
static
void
test1(ArrayList
<?
extends
Number
>
l)
...
{
Integer n
=
new
Integer(
45
);
Number x
=
l.get(
0
);
//
從鏈表中取數據是允許的
l.add(n);
//
錯誤!!
往鏈表里面插入數據是被編譯器嚴格禁止的!!
}
//
2.定義一個
范型方法
。代碼如下:
public
static
<
T
extends
Number
>
void
test2(ArrayList
<
T
>
l)
...
{
Number n
=
l.get(
0
);
T d
=
l.get(
0
);
l.add(d);
//
與上面的方法相比,插入一個范型數據是被
允許
的,相對靈活一些
l.add(n);
//
錯誤!!
只可以插入范型數據,絕不可插入具體類型數據。
}
按照上述代碼的寫法,只要我們對形參添加了一定的約束條件,那么我們在傳遞實參的時候,對實參的嚴格約束就會降低一些。上述代碼都指定了一個類 Number,并用了extends關鍵字,因此,在傳遞實參的時候,凡是從Number繼承的類組成的鏈表,均可以傳遞進去。但上面代碼的注釋中也說的 很清楚,為了不出現運行時錯誤,編譯器會對你調用的方法做嚴格的限制:凡是參數為范型的方法,一律不需調用!!
l.get(
0
)是合法的,因為參數是整型而不是范型;l.add(x)就不合法,因為add函數的參數是范型。但是
定義一個范型方法
還是有一定靈活性的,如果傳入的數據也是范型,編譯器還是認可的,因為范型對范型,類型安全是可以保證的。
從上述代碼可以看出,定義一個范型方法要比Wildcard稍微靈活一些,可以往鏈表中添加T類型的對象,而Wildcard中是不允許往鏈表中添加任何類型的對象的。那么我們還要Wildcard干什么呢?
Wildcard還是有他存在的意義的,那就是,
Wildcard支持另外一個關鍵字super,而范型方法不支持super關鍵字
。換句話說,如果你要實現這樣的功能:“傳入的參數應該是指定類的父類”,范型方法就無能為力了,只能依靠Wildcard來實現。代碼如下:
public
static
void
test5(ArrayList
<?
super
Integer
>
l)
...
{
Integer n
=
new
Integer(
45
);
l.add(n);
//
與上面使用extends關鍵字相反,往鏈表里面插入指定類型的數據是被允許的。
Object x
=
l.get(
0
);
//
從鏈表里取出一個數據仍然是被允許的,不過要賦值給Object對象。
l.add(x);
//
錯誤!!
將剛剛取出的數據再次插入鏈表是不被允許的。
}
這種實現方式的特點我們前面已經說過了,就是對實參的限制更改為:必須是指定類型的父類。這里我們指定了Integer類,那么實參鏈表的元素類型,必須 是Number類及其父類。下面我們重點討論一下上述代碼的第四條語句,為什么將剛剛取出的數據再次插入鏈表不被允許??道理很簡單,剛剛取出的數據被保 存在一個Object類型的引用中,而鏈表的add方法只能接受指定類型Integer及其子類,類型不匹配當然不行。有些人可能立刻會說,我將他強制轉 化為Int
eger類(即
l.add((Integer)x);
)
, 編譯器不就不報錯了嗎?確實,經過強制轉化后,編譯器確實沒意見了。不過這種強制轉化有可能帶來運行時錯誤。因為你傳入的實參,其元素類型是 Integer的父類,比如是Number。那么,存儲在該鏈表中的第一個數據,很有可能是Double或其他類型的,這是合法的。那么你取出的第一個元 素x也會是Double類型。那么你把一個Double類型強制轉化為Integer類型,顯然是一個運行時錯誤。
難道“把取出的元素再插入到鏈表中”這樣一個功能就實現不了嗎?當然可以,不過不能直接實現,要借助范型函數的幫忙,因為在范型函數中,剛剛取出的元素再存回去是不成問題的。定義這樣一個范型函數,我們稱之為幫助函數。代碼如下:
//
幫助函數
public
static
<
T
>
void
helperTest5(ArrayList
<
T
>
l,
int
index)
...
{
T temp
=
l.get(index);
l.add(temp);
}
//
主功能函數
public
static
void
test5(ArrayList
<?
super
Integer
>
l)
...
{
Integer n
=
new
Integer(
45
);
l.add(n);
helperTest5(l,
0
);
//
通過幫助類,將指定的元素取出后再插回去。
}
上述兩個函數結合的原理就是:利用Wildcard的super關鍵字來限制參數的類型(范型函數不支持super,要是支持的話就不用這么麻煩了),然后通過范型函數來完成取出數據的再存儲。
以上就是我學習范型的所有心得。下面再把《Java核心編程》中列出的使用范型時的注意事項列出來(各種操作被禁止的原因就不具體說明了),供大家參考:
//
1、
不可以用一個本地類型(如int float)來替換范型
//
2、
運行時類型檢查,不同類型的范型類是等價的(Pair<String>與Pair<Employee>是屬于同一個類型Pair),
//
這一點要特別注意,即如果a instanceof Pair<String>==true的話,并不代表a.getFirst()的返回值是一個String類型
//
3、
范型類不可以繼承Exception類,即范型類不可以作為異常被拋出
//
4、
不可以定義范型數組
//
5、
不可以用范型構造對象,即first = new T(); 是錯誤的
//
6、
在static方法中不可以使用范型,范型變量也不可以用static關鍵字來修飾
//
7、
不要在范型類中定義equals(T x)這類方法,因為Object類中也有equals方法,當范型類被擦除后,這兩個方法會沖突
//
8、
根據同一個范型類衍生出來的多個類之間沒有任何關系,不可以互相賦值
//
即Pair<Number> p1; Pair<Integer> p2; p1=p2; 這種賦值是錯誤的。
//
9、
若某個范型類還有同名的非范型類,不要混合使用,堅持使用范型類
//
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
//
Pair rawBuddies = managerBuddies; 這里編譯器不會報錯,但存在著嚴重的運行時錯誤隱患
文章來源:
http://wintys.blog.51cto.com/425414/89225
posted on 2009-03-18 12:02
天堂露珠
閱讀(193)
評論(0)
編輯
收藏
所屬分類:
Java
新用戶注冊
刷新評論列表
只有注冊用戶
登錄
后才能發表評論。
網站導航:
博客園
IT新聞
Chat2DB
C++博客
博問
管理
相關文章:
[原]手動打包JAR為可執行文件
[原]手機姓名秀 - NameShow
[轉]Tomcat配置JNDI數據源
[轉]授權標準覆蓋機制
[原]SAX使用示例
[原]方法返回值為數組的另一種表示方法
[原]JNI簡單示例
[原]對Class.getMethod()的調用未經檢查
[原]EL函數的使用
[原]使用Ant實現zip壓縮解壓功能
Copyright ©2025 天堂露珠 Powered by:
博客園
模板提供:
滬江博客
主站蜘蛛池模板:
日韩免费高清播放器
|
国产大片线上免费观看
|
亚洲欧洲日产国产综合网
|
国产亚洲综合视频
|
国产偷国产偷亚洲清高动态图
|
黄网站免费在线观看
|
亚洲精品第一国产综合野
|
久久精品国产亚洲一区二区三区
|
久久久久久一品道精品免费看
|
亚洲变态另类一区二区三区
|
亚洲伊人久久精品影院
|
无人影院手机版在线观看免费
|
搜日本一区二区三区免费高清视频
|
色猫咪免费人成网站在线观看
|
杨幂最新免费特级毛片
|
婷婷精品国产亚洲AV麻豆不片
|
免费观看a级毛片
|
爱丫爱丫影院在线观看免费
|
亚洲精品国产suv一区88
|
亚洲AV无码成人网站久久精品大
|
在线免费观看一级毛片
|
免费毛片a线观看
|
免费的黄色的网站
|
亚洲不卡在线观看
|
亚洲AV无码码潮喷在线观看
|
亚洲精品中文字幕乱码三区
|
18禁成年无码免费网站无遮挡
|
七色永久性tv网站免费看
|
在线91精品亚洲网站精品成人
|
亚洲性69影院在线观看
|
亚洲深深色噜噜狠狠爱网站
|
国产男女猛烈无遮档免费视频网站
|
16女性下面扒开无遮挡免费
|
成人免费视频一区二区
|
国产亚洲sss在线播放
|
亚洲国产女人aaa毛片在线
|
亚洲国产精品自在拍在线播放
|
巨胸喷奶水视频www网免费
|
亚欧人成精品免费观看
|
国产免费无码一区二区
|
eeuss影院ss奇兵免费com
|