Potter:I am not for learning and learning, but for study and work
看不少文章都沒有弄得這個泛型到底是做什麼的,怎麼用?下面這篇文章就用最通俗的話來介紹...一看就明白
規則和限制:
1、泛型的型別引數只能是類型別(包括自定義類),不能是簡單型別。
2、同一種泛型可以對應多個版本(因為引數型別是不確定的),不同版本的泛型類例項是不相容的。
3、泛型的型別引數可以有多個。
4、泛型的引數型別可以使用extends語句,例如<T extends superclass>。習慣上稱為“有界型別”。
5、泛型的引數型別還可以是萬用字元型別。例如Class<?> classType = Class.forName("java.lang.String");
泛型還有介面、方法等等,內容很多,需要花費一番功夫才能理解掌握並熟練應用。在此給出我曾經瞭解泛型時候寫出的兩個例子(根據看的印象寫的),實現同樣的功能,一個使用了泛型,一個沒有使用,通過對比,可以很快學會泛型的應用,學會這個基本上學會了泛型70%的內容。
例子:/** *@Description: *@author Potter *@date 2012-3-5 下午10:16:28 *@version V1.0 */ public class Gen<T> { private T ob; public Gen(T ob){ this.ob=ob; } public void showType(){ System.out.println("T的實際型別:"+ob.getClass()); } public T getOb() { return ob; } public void setOb(T ob) { this.ob = ob; } } class GenDemo{ public static void main(String[] args){ Gen<Integer> intOb=new Gen<Integer>(88); intOb.showType(); int i=intOb.getOb(); System.out.println("i value="+i); System.out.println("---------"); Gen<String>strOb=new Gen<String>("hello Gen!"); strOb.showType(); String s=strOb.getOb(); System.out.println("s value="+s); } }
例子二:
/** *@Description: *@author Potter *@date 2012-3-5 下午10:24:49 *@version V1.0 */ public class Gen2 { private Object ob; public Gen2(Object ob){ this.ob=ob; } public void showType(){ System.out.println("T的實際型別是:"+ob.getClass().getName()); } public Object getOb() { return ob; } public void setOb(Object ob) { this.ob = ob; } } class GenDemo2{ public static void main(String[] args){ Gen2 intOb=new Gen2(new Integer(88)); intOb.showType(); int i=(Integer)intOb.getOb(); System.out.println("i value="+i); System.out.println("-----------"); Gen2 strOb=new Gen2("Hello Gen!"); strOb.showType(); String s=(String)strOb.getOb(); System.out.println("s value="+s); } }
執行結果:
兩個例子執行Demo結果是相同的,控制檯輸出結果如下:
T的實際型別是:
java.lang.Integer
value= 88
----------------------------------
T的實際型別是: java.lang.String
value= Hello Gen!
Process finished with exit code 0
看明白這個,以後基本的泛型應用和程式碼閱讀就不成問題了。
逐漸深入泛型:
1、沒有任何重構的原始程式碼:
有兩個類如下:要構造兩個類的物件,並打印出各自的成員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;
}
}
以上的程式碼實在無聊,就不寫如何實現了。
2、對上面的兩個類進行重構,寫成一個類
因為上面的類中,成員和方法的邏輯都一樣,就是型別不一樣,因此考慮重構。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("Hello generics!");
ObjectFoo douFoo=new ObjectFoo(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());
}
運算結果:
解說:在Java 5之前,為了讓類有通用性,往往將引數型別、返回型別設定為Object型別,當獲取這些返回型別來使用時候,必須將其“強制”轉換為原有的型別或者介面,然後才可以呼叫物件上的方法。3、java1.5泛型來實現
強制型別轉換很麻煩,我還要事先知道各個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;
}
}
class ObjectFooDemo{
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
[email protected]
和使用“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型別,這也是要強制轉換的原因。
泛型的高階應用:
1、在上面的例子中,由於沒有限制class GenericsFoo<T>型別持有者T的範圍,實際上這裡的限定型別相當於Object,這和“Object泛型”實質是一樣的。限制比如我們要限制T為集合介面型別。只需要這麼做:
class GenericsFoo<T extends Collection>,這樣類中的泛型T只能是Collection介面的實現類,傳入非Collection介面編譯會出錯。
注意:<T extends Collection>這裡的限定使用關鍵字 extends,後面可以是類也可以是介面。但這裡的extends已經不是繼承的含義了,應該理解為T型別是實現Collection介面的型別,或者T是繼承了XX類的型別。
下面繼續對上面的例子改進,我只要實現了集合介面的型別:
import java.util.ArrayList;
import java.util.Collection;
/**
*@Description:
*@author Potter
*@date 2012-3-5 下午10:49:54
*@version V1.0
*/
public class CollectGenFoo<T extends Collection> {
private T x;
public CollectGenFoo(T x) {
this.x=x;
}
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
}
class CollectGenFooDemo{
public static void main(String[]args){
CollectGenFoo<ArrayList> listFoo=null;
listFoo=new CollectGenFoo<ArrayList>(new ArrayList());
//出錯了,不讓這麼幹
//CollectGenFoo<Collection> listFoo=null;
//listFoo=new CollectGenFoo<ArrayList>(new ArrayList());
// CollectionGenFoo<Collection> listFoo = null;
// listFoo=new CollectionGenFoo<ArrayList>(new ArrayList());
System.out.println("例項化成功!");
}
}
當前看到的這個寫法是可以編譯通過,並執行成功。可是註釋掉的兩行加上就出錯了,因為<T extends Collection>這麼定義型別的時候,就限定了構造此類例項的時候T是確定的一個型別,這個型別實現了Collection介面,但是實現 Collection介面的類很多很多,如果針對每一種都要寫出具體的子類型別,那也太麻煩了,我乾脆還不如用Object通用一下。別急,泛型針對這種情況還有更好的解決方案,那就是“萬用字元泛型”。
2、萬用字元泛型
為了解決型別被限制死了不能動態根據例項來確定的缺點,引入了“萬用字元泛型”,針對上面的例子,使用通配泛型格式為<? extends Collection>,“?”代表未知型別,這個型別是實現Collection介面。那麼上面實現的方式可以寫為:
class CollectGenFooDemo{
public static void main(String[]args){
CollectGenFoo<ArrayList> listFoo=null;
listFoo=new CollectGenFoo<ArrayList>(new ArrayList());
//CollectGenFoo<Collection> listFoo=null;
//listFoo=new CollectGenFoo<ArrayList>(new ArrayList());
CollectGenFoo<? extends Collection>listFoo1=null;
listFoo1=new CollectGenFoo<ArrayList>(new ArrayList());
System.out.println("例項化成功");
}
}
注意:
1、如果只指定了<?>,而沒有extends,則預設是允許Object及其下的任何Java類了。也就是任意類。
2、萬用字元泛型不單可以向下限制,如<? extends Collection>,還可以向上限制,如<? super Double>,表示型別只能接受Double及其上層父類型別,如Number、Object型別的例項。
3、泛型類定義可以有多個泛型引數,中間用逗號隔開,還可以定義泛型介面,泛型方法。這些都泛型類中泛型的使用規則類似。
泛型方法:
是否擁有泛型方法,與其所在的類是否泛型沒有關係。要定義泛型方法,只需將泛型引數列表置於返回值前。如:
/**
*@Description:
*@author Potter
*@date 2012-3-5 下午11:56:51
*@version V1.0
*/
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方法需要使用泛型能力,必須使其成為泛型方法。