基本介紹
- 中文名:Java泛型
- 外文名:Java generics
- 套用學科:計算機軟體
- 軟體語言:Java
- 適用版本:Java SE 1.5以上
規則限制,深入泛型,原始代碼,重構,實現,高級套用,限制泛型,多接口限制,通配符泛型,泛型方法,關於Java,
規則限制
1、泛型的類型參數只能是類類型(包括自定義類),不能是簡單類型。
2、同一種泛型可以對應多個版本(因為參數類型是不確定的),不同版本的泛型類實例是不兼容的。
3、泛型的類型參數可以有多個。
4、泛型的參數類型可以使用extends語句,例如<T extends superclass>。習慣上稱為“有界類型”。
5、泛型的參數類型還可以是通配符類型。例如Class<?> classType = Class.forName("java.lang.String");
泛型還有接口、方法等等,內容很多,需要花費一番功夫才能理解掌握並熟練套用。在此給出我曾經了解泛型時候寫出的兩個例子(根據看的印象寫的),實現同樣的功能,一個使用了泛型,一個沒有使用,通過對比,可以很快學會泛型的套用,學會這個基本上學會了泛型70%的內容。
例子一:使用了泛型
class Gen<T> { private T ob; // 定義泛型成員變數 public Gen(T ob) { this.ob = ob; } public T getOb() { return ob; } public void setOb(T ob) { this.ob = ob; } public void showType() { System.out.println("T的實際類型是: " + ob.getClass().getName()); }}public class GenDemo { public static void main(String[] args) { // 定義泛型類Gen的一個Integer版本 Gen<Integer> intOb = new Gen<Integer>(88); intOb.showType(); int i = intOb.getOb(); System.out.println("value= " + i); System.out.println("----------------------------------"); // 定義泛型類Gen的一個String版本 Gen<String> strOb = new Gen<String>("Hello Gen!"); strOb.showType(); String s = strOb.getOb(); System.out.println("value= " + s); }}
例子二:沒有使用泛型
class Gen2 { private Object ob; // 定義一個通用類型成員 public Gen2(Object ob) { this.ob = ob; } public Object getOb() { return ob; } public void setOb(Object ob) { this.ob = ob; } public void showTyep() { System.out.println("T的實際類型是: " + ob.getClass().getName()); }}public class GenDemo2 { public static void main(String[] args) { // 定義類Gen2的一個Integer版本 Gen2 intOb = new Gen2(new Integer(88)); intOb.showTyep(); int i = (Integer) intOb.getOb(); System.out.println("value= " + i); System.out.println("---------------------------------"); // 定義類Gen2的一個String版本 Gen2 strOb = new Gen2("Hello Gen!"); strOb.showTyep(); String s = (String) strOb.getOb(); System.out.println("value= " + s); }}
運行結果:
兩個例子運行Demo結果是相同的,控制台輸出結果如下:
T的實際類型是:java.lang.Integer
value= 88
----------------------------------
T的實際類型是: java.lang.String
value= Hello Gen!
Process finished with exit code 0
看明白這個,以後基本的泛型套用和代碼閱讀就不成問題了。
深入泛型
原始代碼
有兩個類如下,要構造兩個類的對象,並列印出各自的成員x。
public class StringFoo { private String x; public StringFoo(String x) { this.x = x; } public String getX() { return x; } public void setX(String x) { this.x = x; }}public class DoubleFoo { private Double x; public DoubleFoo(Double x) { this.x = x; } public Double getX() { return x; } public void setX(Double x) { this.x = x; }}
重構
因為上面的類中,成員和方法的邏輯都一樣,就是類型不一樣,因此考慮重構。Object是所有類的父類,因此可以考慮用Object做為成員類型,這樣就可以實現通用了,實際上就是“Object泛型”,暫時這么稱呼。
public class ObjectFoo { private Object x; public ObjectFoo(Object x) { this.x = x; } public Object getX() { return x; } public void setX(Object x) { this.x = x; }}
寫出Demo方法如下:
public class ObjectFooDemo { public static void main(String args[]) { ObjectFoo strFoo = new ObjectFoo(new String("Hello Generics!")); ObjectFoo douFoo = new ObjectFoo(new Double(new Double("33"))); ObjectFoo objFoo = new ObjectFoo(new Object()); System.out.println("strFoo.getX=" + (String) strFoo.getX()); System.out.println("douFoo.getX=" + (Double) douFoo.getX()); System.out.println("objFoo.getX=" + objFoo.getX()); }}
運行結果如下:
strFoo.getX=Hello Generics!
douFoo.getX=33.0
objFoo.getX=java.lang.Object@15db9742
解說:在Java 5之前,為了讓類有通用性,往往將參數類型、返回類型設定為Object類型,當獲取這些返回類型來使用時候,必須將其“強制”轉換為原有的類型或者接口,然後才可以調用對象上的方法。
實現
強制類型轉換很麻煩,我還要事先知道各個Object具體類型是什麼,才能做出正確轉換。否則,要是轉換的類型不對,比如將“Hello Generics!”字元串強制轉換為Double,那么編譯的時候不會報錯,可是運行的時候就掛了。那有沒有不強制轉換的辦法----有,改用 Java5泛型來實現。
class GenericsFoo<T> { private T x; public GenericsFoo(T x) { this.x = x; } public T getX() { return x; } public void setX(T x) { this.x = x; }}public class GenericsFooDemo { public static void main(String args[]) { GenericsFoo<String> strFoo = new GenericsFoo<String>("Hello Generics!"); GenericsFoo<Double> douFoo = new GenericsFoo<Double>(new Double("33")); GenericsFoo<Object> objFoo = new GenericsFoo<Object>(new Object()); System.out.println("strFoo.getX=" + strFoo.getX()); System.out.println("douFoo.getX=" + douFoo.getX()); System.out.println("objFoo.getX=" + objFoo.getX()); }}
運行結果:
strFoo.getX=Hello Generics!
douFoo.getX=33.0
objFoo.getX=java.lang.Object@15db9742
和使用“Object泛型”方式實現結果的完全一樣,但是這個Demo簡單多了,裡面沒有強制類型轉換信息。
下面解釋一下上面泛型類的語法:
使用<T>來聲明一個類型持有者名稱,然後就可以把T當作一個類型代表來聲明成員、參數和返回值類型。
當然T僅僅是個名字,這個名字可以自行定義。
class GenericsFoo<T> 聲明了一個泛型類,這個T沒有任何限制,實際上相當於Object類型,實際上相當於 class GenericsFoo<T extends Object>。
與Object泛型類相比,使用泛型所定義的類在聲明和構造實例的時候,可以使用“<實際類型>”來一併指定泛型類型持有者的真實類型。類如
GenericsFoo<Double> douFoo=new GenericsFoo<Double>(new Double("33"));
當然,也可以在構造對象的時候不使用尖括弧指定泛型類型的真實類型,但是你在使用該對象的時候,就需要強制轉換了。比如:GenericsFoo douFoo=new GenericsFoo(new Double("33"));
實際上,當構造對象時不指定類型信息的時候,默認會使用Object類型,這也是要強制轉換的原因。
高級套用
限制泛型
在上面的例子中,由於沒有限制class GenericsFoo<T>類型持有者T的範圍,實際上這裡的限定類型相當於Object,這和“Object泛型”實質是一樣的。限制比如我們要限制T為集合接口類型。只需要這么做:
class GenericsFoo<T extends Collection>,這樣類中的泛型T只能是Collection接口的實現類,傳入非Collection接口編譯會出錯。
注意:<T extends Collection>這裡的限定使用關鍵字extends,後面可以是類也可以是接口。但這裡的extends已經不是繼承的含義了,應該理解為T類型是實現Collection接口的類型,或者T是繼承了XX類的類型。
下面繼續對上面的例子改進,我只要實現了集合接口的類型:
public class CollectionGenFoo<T extends Collection> { private T x; public CollectionGenFoo(T x) { this.x = x; } public T getX() { return x; } public void setX(T x) { this.x = x; }}
實例化的時候可以這么寫:
public class CollectionGenFooDemo { public static void main(String args[]) { CollectionGenFoo<ArrayList> listFoo = null; listFoo = new CollectionGenFoo<ArrayList>(new ArrayList()); // 出錯了,不讓這么乾。 // 原來作者寫的這個地方有誤,需要將listFoo改為listFoo1 // 需要將CollectionGenFoo<Collection>改為CollectionGenFoo<ArrayList> // CollectionGenFoo<Collection> listFoo1 = null; // listFoo1=new CollectionGenFoo<ArrayList>(new ArrayList()); System.out.println("實例化成功!"); }}
當前看到的這個寫法是可以編譯通過,並運行成功。可是注釋掉的兩行加上就出錯了,因為<T extends Collection>這么定義類型的時候,就限定了構造此類實例的時候T是確定的一個類型,這個類型實現了Collection接口,但是實現 Collection接口的類很多很多,如果針對每一種都要寫出具體的子類類型,那也太麻煩了,我乾脆還不如用Object通用一下。別急,泛型針對這種情況還有更好的解決方案,那就是“通配符泛型”。
多接口限制
雖然Java泛型簡單的用 extends 統一的表示了原有的 extends 和 implements 的概念,但仍要遵循套用的體系,Java 只能繼承一個類,但可以實現多個接口,所以你的某個類型需要用 extends 限定,且有多種類型的時候,只能存在一個是類,並且類寫在第一位,接口列在後面,也就是:
<T extends SomeClass & interface1 & interface2 & interface3>
這裡的例子僅演示了泛型方法的類型限定,對於泛型類中類型參數的限制用完全一樣的規則,只是加在類聲明的頭部,如:
public class Demo<T extends Comparable & Serializable> { // T類型就可以用Comparable聲明的方法和Seriablizable所擁有的特性了}
通配符泛型
為了解決類型被限制死了不能動態根據實例來確定的缺點,引入了“通配符泛型”,針對上面的例子,使用通配泛型格式為<? extends Collection>,“?”代表未知類型,這個類型是實現Collection接口。那么上面實現的方式可以寫為:
public class CollectionGenFooDemo { public static void main(String args[]) { //CollectionGenFoo<ArrayList> listFoo = null; //listFoo = new CollectionGenFoo<ArrayList>(new ArrayList()); CollectionGenFoo<?> listFoo1 = null; listFoo1=new CollectionGenFoo<ArrayList>(new ArrayList()); System.out.println("實例化成功!"); }}
注意:
1、如果只指定了<?>,而沒有extends,則默認是允許Object及其下的任何Java類了。也就是任意類。
2、通配符泛型不單可以向上限制,如<? extends Collection>,還可以向下限制,如<? super Double>,表示類型只能接受Double及其上層父類類型,如Number、Object類型的實例。
3、泛型類定義可以有多個泛型參數,中間用逗號隔開,還可以定義泛型接口,泛型方法。這些都與泛型類中泛型的使用規則類似。
泛型方法
是否擁有泛型方法,與其所在的類是否泛型沒有關係。要定義泛型方法,只需將泛型參數列表置於返回值前。如:
public class ExampleA { public <T> void f(T x) { System.out.println(x.getClass().getName()); } public static void main(String[] args) { ExampleA ea = new ExampleA(); ea.f(" "); ea.f(10); ea.f('a'); ea.f(ea); }}
輸出結果:
java.lang.String
java.lang.Integer
java.lang.Character
ExampleA
使用泛型方法時,不必指明參數類型,編譯器會自己找出具體的類型。泛型方法除了定義不同,調用就像普通方法一樣。
需要注意,一個static方法,無法訪問泛型類的類型參數,所以,若要static方法需要使用泛型能力,必須使其成為泛型方法。
關於Java
詹姆斯·戈士林博士以“Java技術之父”聞名於世。他是Java技術的創始人, 作為Sun研究院院士,他親手設計了Java語言,完成了Java技術的原始編譯器和虛擬機。在他的帶領下,Java現已成為網際網路的標準編程模式以及分散式企業級套用的事實標準,其跨平台的技術優勢為網路計算帶來了劃時代的變革。戈士林博士積極致力於軟體開發工具的研究,以使軟體開發工具的功能更強大,更容易為開發人員所使用,確保套用、服務開發的迅速完成。
Java技術是Sun公司在1995年5月正式推出的。十年多來,Java已從程式語言發展成為全球第一大通用開發平台。Java技術已為計算機行業主要公司所採納,同時也被越來越多的國際技術標準化組織所接受。1999年,Sun推出了以Java 2平台為核心的J2EE、J2SE和J2ME三大平台。隨著三大平台的迅速推進,在世界上形成了一股巨大的Java套用浪潮。同時,Java技術還引發了一場無法停止的大變革,為整個Java社團帶來了巨大的潮水般的商業機會。