国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Java語言與Generics

2019-11-18 14:15:49
字體:
來源:轉載
供稿:網友

內容:

一 Generics簡介
二 Generics與java語言的發展
三 Java中Generics的使用
四 Generics的設計和實現
五 結論
參考資料
關于作者


在 Java 專區還有:

教學
工具與產品
代碼與組件
所有文章
實用技巧




歐陽辰 (yeekee@sina.com)


一 Generics簡介
Generics是程序設計語言的一種技術,指將程序中數據類型進行參數化,它本質上是對程序的數據類型進行一次抽象,擴展語言的表達能力,同時支持更大粒度的代碼復用。對于一些數據類型參數化的類和方法來說,它們往往具有更好的可讀性、可復用性和可靠性。在設計集合類和它們的抽象操作時,往往需要將它們定義為與具體數據類型無關,在這種情況下,使用Generics就是非常適合的。舉例來說,假如我們需要設計一個Stack類,有時需要元素為int類型的Stack,有時可能需要元素為Boolean或者Object類型的Stack。假如不使用Generics,我們通常需要定義不同的多個類,或者通過繼續來實現。通過繼續實現往往引起數據類型的轉換問題,本文稍后對其進行分析。假如使用Generics技術,將Stack的元素類型進行參數化,那么Stack類的只需要實現一個版本,當需要某元素類型的Stack時,可以將類型作為參數來創建Stack對象。

二Generics與Java語言的發展
Generics技術很早就被提出來了,但是Generics的實現和推廣并非一帆風順。這是因為Generics對語言規范的修改較大,對于編譯技術要求也較高,另外,90年代,所有語言都在積極擴展面向對象技術,并沒有太多時間關注Generics。到目前為止,只少量語言支持Generics,例如Ada, C++, Eiffel等。在C++中, Template被用來現實Generics,同時C++還提供了標準模板庫(STL,Standard Template Library)供開發人員使用,極大提高了編程的效率,因此Generics經常又被混稱作Template。

但是,到目前JDK1.4.1為止,Java語言不支持Generics。目前,對于數據類型的參數化,Java不能夠直接實現,而是通常采用繼續方式間接實現。由于Java中,所有類都是Object的子類,因此假如需要支持多種類型時,我們可以將類型定義為Object,在使用的時候,可以通過父類和子類進行相互轉化來實現。以上面的Stack為例,我們可以將Stack的類型定義為Object類型,那么所有的Java對象都可以放在這個Stack中,在存取對象時候,我們可以將對象強行轉化成所需要的類型。但是,這種方式也會帶來一些問題。

編譯時,類型檢查較為寬松,很多問題只有運行時候才能發現
很難實現復雜的類型參數化,缺少表達能力
增加使用者的麻煩,使用者需要對數據類型進行手工轉換,并且進行類型檢查
由于Java區分Object和基本類型,因此這種方式不輕易支持基本類型。



雖然當前Java語言規范并不支持Generics,但是一些組織和個人通過擴展Java語言的方式來實現Generics,其中比較聞名的有GJ,PolyJ和NextGen。Java語言標準的制定組織Java Community PRocess(JCP)也早已收到關于在Java語言中支持Generics的建議,并且一直在討論是否在Java語言支持Generics。其中,一個比較重要的里程碑是Gilad Bracha博士等在2001年提出的提議。

Generics的實現對于Java語言本身、Class文件格式和JVM構造都有較大的影響;另外,Generics在C++中的實現也存在一些問題,這些問題都應該在Java中盡量避免,最后,還有一部分人認為Generics的引入會損失Java語言的簡潔性。基于這些考慮,目前Java語言仍然不能夠支持Generics,但是根據一些相關人員的預計,2003年年底推出JDK1.5很可能要支持Generics。

三Java中Generics的使用
在這一章中,我們將預覽一些Java的Generics的用法,雖然這些技術并沒有正式推出,但是根據Java Generics提議的草稿版本來看,這些用法都比較穩定且成熟,預計它們與正式規范不會有太大差別。另外,這些Generics程序通過專用的編譯器,可以轉換成兼容的Java類文件,并且可以在以前的JRE環境下運行,保證了向前兼容。本章節的部分內容來自Java語言Generics提議的草稿版本,另外一些例子也參考了GJ的運行結果。

1) Generics的定義:
為了使用Generics,首先必須定義支持Generics的類,接口或者方法,它與C++語言的模板的語法類似。<>用于包含參數化類型,參數化類型用Java標識符標識表示,通常使用大寫字母,例如T,A,B等。

1.1) 類和接口定義
以下是最簡單的Generics類定義,定義了一個參數化類型T1:
interface MyList<T1> {….. }

以下是支持多個參數化類型的接口:
interface MyList<T1,T2,T3> {….. }

Java支持帶有限制的參數化類型,這意味著在構造該類對象的時候,實際類型必須滿足限制條件。在下面的例子中,T1的類型必須實現類Comparable接口,T2類型必須為Component類的子類,否則將構造失敗。這些限制檢查工作通常在編譯的時候就可以進行。
interface MyList<T1 implements Comparable, T2 extends Component> {}

復雜的定義可以帶有限定的聲明,甚至可以使用向前引用。
class Test<A implements ConvertibleTo<B>, B implements ConvertibleTo<A>{}

對于class的定義,基本與interface相同,此處不再詳述。

1.2) 方法的定義
在方法中,通過定義參數化類型,可以提高方法的抽象級別,提高其可復用性。方法的參數化類型列表放在方法修飾符的后面,返回值的前面。在方法的參數中和方法體中,就可以直接使用參數化類型了。以下就是一個方法的例子。

public static <Elem> void swap(Elem[] a,int i,int j)
Elem temp=a[i];
a[i]=a[j]
a[j]=temp;
}





帶限定的Generics方法定義例子。

public static <Elem implements Comparable> void swap(Elem[] a,int I,int j)
Elem temp=a[i];
a[i]=a[j]
a[j]=temp;
}





>
1.3) 實際例子
一但Java語言支持了Generics,Collection中的大部分類將用Generics方式重寫,事實上,在一些支持Generics的Java擴展中,這些類已經被重寫了。現在我們給出一個GJ中,使用Generics重寫的Hashtable的定義。

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, java.lang.Cloneable, java.io.Serializable {
public V put(K key, V value) {……}
…….
}





與以前的Hashtable定義不同的是,它增加了兩個用于表達Key和Value參數化類型(K,V),由于Dictionary和Map都將支持Generics,因此它們都使用Generics的表達方式。

2) Generics的使用:

2.1) 創建對象
Generics類在使用之前,必須按照定義進行初始化,設置參數化類型的實際類型,以下是構造Hashtable和Vector的一些例子。

Hashtable<Integer,String> ht1=new Hashtable<Integer,String>();
Hashtable<Integer,String> ht2=null;
Vector<String> v1=new Vector<String>();
Vector<Integer> v2=new Vector<Integer>();






2.2) Generics對象的類
在上例中,對于v1和v2對象來說,它們都是Vector類的對象,它們有相同的Class類型,換句話說,在運行時,以下表達式為真。雖然它們使用不同的參數類型創建,但是它們的Class類型是相同的。
Assert(V1.getClass().equals(v2.getClass()));

但是,假如我們定義一個 帶有Vector<String>參數的方法,在調用該方法時,傳入一個Vector<Integer>的對象,這將會導致編譯失敗。這是因為在編譯時,這兩個對象被當作不同的類型。

void method(Vector<String> v) {};

Vector<Integer> v2=new Vector<Integer>();
method(v2);//編譯失敗






2.3) 類型檢查
在構造一個Generics對象時,編譯器將首先檢查參數化類型是否有效,例如是否滿足限制條件等,確定所有參數化類型,然后編譯器將在使用這些參數化類型的地方進行類型檢查,假如符合定義,那么編譯通過,否則將編譯失敗,報告類型檢查錯誤。因此,通過這種方式,編譯器可以檢查出很多類型不匹配的錯誤,避免開發人員的錯誤,這也是Generics的重要優點之一。以下是類型檢查的一些例子:

Vector<String> strvector=new Vector<String>();
Vector<Integer> intvector=new Vector<Integer>();
strvector.add(new String());//OK
strvector.add(new Object());//編譯失敗,需要String類
strvector.add(new Integer());//編譯失敗,需要String類
intvector.add(new Integer());//OK
intvector.add(new String());//編譯失敗,需要Integer類






2.4) 類的強制轉換
對于Generics類的強制轉化的原理,我們可以使用通用的轉化規則進行操作。需要注重的一點是,同一個Generics類所定義的不同參數化類型的對象之間是不能進行轉化的。例如Vector<Object>和Vector<String>之間就是不能轉化的。另外,Object也不能夠轉化成Generics類型,但是Generics類可以轉化成Object。

以下是一些轉化的例子

Class Dictionary<A,B> extends Object{}
Class Hashtable<A,B> extends Dictionary<A,B> {}
Dictionary<String,Integer> d=new Dictionary<String,Integer>();
Hashtable<String,Integer> h=new Hashtable <String,Integer>();
Hashtable<Float,Double> hfd=new Hashtable<Float,Double>();
Object o=new Object();
1) d= (Dictionary<String,Integer>)h//編譯成功,運行成功;它們具有父子類關系。
2) h=(Hashtable<String,Integer>)d;// 編譯成功,運行失敗;它們具有父子類關系。
3) h=(Hashtable<String,Integer>)o;//編譯失敗,Object不能轉化成Generics類;
4) hfd=(Hashtable<Float,Double>)d;//編譯失敗;






四Generics的設計和實現

1) Java的Generics與C++的Template
由于Java的Generics設計在C++的Template之后,因此Java的Generics設計吸取Template的很多經驗和教訓,非凡是Generics避免了一些Template已知的一些問題。首先,與Template不同的是,Generics的聲明是需要進行類型檢查的,而Template不提供這一功能,這使得Generics的使用更加安全。另外,Java的Generics程序只需要編譯一次,以后所有程序就可以復用這個類字節碼,而Template的實現是為每一個使用Template變量編譯成一個新類,這會引起一定的冗余代碼。

2) Generics的實現方案
Generics Java目前有很多不同的實現,比較聞名的有GJ,PolyJ和NextGen。其中,GJ(Generic Java)是Gilad Bracha博士等設計和開發的支持Generics的Java編譯器,它是較早,且較全面的Generics的解決方案,實際上,GJ是目前Java語言的一個擴展,主要對編譯器進行了擴展,以支持帶有Generics 的Java 程序。

GJ的工作原理本質上就是消除程序中的Generics語法,并將其轉化成等價的無Generics的程序,這樣編譯的結果就是傳統的類字節碼,它們可以在傳統的JVM中的運行,保證了向前兼容。編譯的過程就是將所有參數化類型的變量都替換為Object,通過這種方式消除所有的參數化類型。由于Java中,所有對象都可以轉換程Object的對象,因此通過這種方式可以消除參數化類型,但這種方法也有一個問題,那就是無法處理基本類型,因為Object與基本類型無法相互轉換。在消除了參數化類型后,在適當的地方還需要加上一些類型檢查和轉化語句。

例如Stack類的Generics源程序可能如下表示:

public class Stack<A> {
public void push(A elem){…. }
public A pop(){…..}
}






經過GJ改寫后,程序將變為如下:

public class Stack {
public void push(Object elem){….}
public Object pop(){….}
}






在使用Generics類的時候,我們首先設置參數化類型的值,在下例子我們傳入Button類型。

Stack<Button> s = new Stack<Button>();
Button b = new Button("OK");
s.push(b);
b=s.pop();






GJ在編譯以上代碼時,在消除<>后,首先需要檢查b是否能夠轉換成Button,假如b不能夠轉換成Button,編譯器將報類型轉化錯誤。同時, 在pop方法的返回值處,我們需要將Object類型顯式轉換為Button類型。以下就是GJ轉化的結果;

Stack s = new Stack();
Button b = new Button("b");
s.push(b);
b=(Button)s.pop();






以上只描述了一些基本原理,在真正的實現中,GJ還需要處理繼續,約束和類型轉換等復雜問題。從實現的效果來看,GJ是非常成功地支持了Generics,并且提供了通過Generics改寫的java.utils.collection包。更多的實現技術,請參考相關資料。

五 結論
Generics的出現將改變一些Java程序員的編程風格,以往所有不確定類型的對象都被定義為Object,需要使用對象時,通過強制類型轉化獲取,而Generics的出現將可用于治理一些類型抽象的類,讓編譯器來檢查更多的類型匹配的問題,以減輕程序員的負擔。但同時,Generics的引入,增加了Java程序的抽象程度,增加程序理解的難度。

相關資料:

Gilad Bracha, Norman Cohen,Christian Kemper etc, Adding Generics to the Java Programming Language Participant draft Specification, 2001,
http://java.sun.com/aboutJava/communityprocess/review/jsr014/
Paul Mingardi, Prepari8java.sun.com/developer/technicalArticles/releases/generics/">http://developer.java.sun.com/developer/technicalArticles/releases/generics/
Keith Turner, Catching more errors at compile time with Generic Java,IBM DeveloperWorks ,2001
http://www-106.ibm.com/developerworks/library/j-genjava.Html
Generic Java (GJ)
http://www.research.avayalabs.com/user/wadler/gj/


關于作者
歐陽辰,2001年畢業于北京大學計算機系,獲碩士學位,SCJP(Sun Certificated Java Programmer),現任某公司軟件工程師,長期從事java軟件的研究和開發工作,已發表多篇Java相關的技術文章。聯系方式yeekee@sina.com

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 凤凰县| 集安市| 靖安县| 家居| 澜沧| 新源县| 新津县| 安陆市| 英吉沙县| 都匀市| 天津市| 晋江市| 凯里市| 亚东县| 昌宁县| 林周县| 塔河县| 新野县| 湘潭市| 宜兰县| 体育| 太白县| 韶山市| 石景山区| 景宁| 织金县| 肥城市| 资溪县| 霍州市| 隆子县| 池州市| 黄陵县| 肃北| 白城市| 泰安市| 凤台县| 黄浦区| 新绛县| 中卫市| 青川县| 镇平县|