java泛型

泛型是Java SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。這種參數類型能夠用在類、接口和方法的建立中,分別稱爲 泛型類、泛型接口、泛型方法。 Java語言引入泛型的好處是安全簡單。
在Java SE 1.5以前,沒有泛型的 狀況的下,經過對類型Object的引用來實現參數的「任意化」,「任意化」帶來的缺點是要作顯式的 強制類型轉換,而這種轉換是要求開發者對 實際參數類型能夠預知的狀況下進行的。對於強制類型轉換錯誤的狀況, 編譯器可能不提示錯誤,在運行的時候纔出現異常,這是一個安全隱患。
泛型的好處是在編譯的時候檢查 類型安全,而且全部的 強制轉換都是自動和 隱式的,以提升代碼的重用率。

規則限制

一、泛型的類型參數只能是類類型(包括自定義類),不能是簡單類型。
二、同一種泛型能夠對應多個版本(由於參數類型是不肯定的),不一樣版本的 泛型類實例是不兼容的。
三、泛型的類型參數能夠有多個。
四、泛型的參數類型可使用extends語句,例如<T extends superclass>。習慣上稱爲「有界類型」。
五、泛型的參數類型還能夠是 通配符類型。例如Class<?> classType = Class.forName("java.lang.String");
泛型還有接口、方法等等,內容不少,須要花費一番功夫才能理解掌握並熟練應用。在此給出我曾經瞭解泛型時候寫出的兩個例子(根據看的印象寫的),實現一樣的功能,一個使用了泛型,一個沒有使用,經過對比,能夠很快學會泛型的應用,學會這個基本上學會了泛型70%的內容。
例子一:使用了泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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);
     }
}
例子二:沒有使用泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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泛型」,暫時這麼稱呼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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方法以下:
1
2
3
4
5
6
7
8
9
10
public  class  ObjectFooDemo {
     public  static  void  main(String args[]) {
         ObjectFoo strFoo =  new  ObjectFoo( new  StringFoo( "Hello Generics!" ));
         ObjectFoo douFoo =  new  ObjectFoo( new  DoubleFoo( new  Double( "33" )));
         ObjectFoo objFoo =  new  ObjectFoo( new  Object());
         System.out.println( "strFoo.getX="  + (StringFoo) strFoo.getX());
         System.out.println( "douFoo.getX="  + (DoubleFoo) douFoo.getX());
         System.out.println( "objFoo.getX="  + objFoo.getX());
     }
}
運行結果以下:
strFoo.getX=StringFoo@5d748654
douFoo.getX=DoubleFoo@d1f24bb
objFoo.getX=java.lang.Object@19821f
解說:在Java 5以前,爲了讓類有通用性,每每將參數類型、返回類型設置爲Object類型,當獲取這些返回類型來使用時候,必須將其「強制」轉換爲原有的類型或者接口,而後才能夠調用對象上的方法。
強制類型轉換很麻煩,我還要事先知道各個Object具體類型是什麼,才能作出正確轉換。不然,要是轉換的類型不對,好比將「Hello Generics!」字符串 強制轉換爲Double,那麼編譯的時候不會報錯,但是運行的時候就掛了。那有沒有不強制轉換的辦法----有,改用 Java5泛型來實現。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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@19821f
和使用「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類的類型。
下面繼續對上面的例子改進,我只要實現了集合接口的類型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
     }
}
實例化的時候能夠這麼寫:
1
2
3
4
5
6
7
8
9
10
11
12
public  class  CollectionGenFooDemo {
     public  static  void  main(String args[]) {
         CollectionGenFoo<ArrayList> listFoo =  null ;
         listFoo =  new  CollectionGenFoo<ArrayList>( new  ArrayList());
         // 出錯了,不讓這麼幹。
         // 須要將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>
這裏的例子僅演示了泛型方法的類型限定,對於泛型類中類型參數的限制用徹底同樣的規則,只是加在類聲明的頭部,如:
1
2
3
public  class  Demo<T  extends  Comparable & Serializable> {
     // T類型就能夠用Comparable聲明的方法和Seriablizable所擁有的特性了
}

通配符泛型

爲了解決類型被限制死了不能動態根據實例來肯定的缺點,引入了「通配符泛型」,針對上面的例子,使用通配泛型格式爲<? extends Collection>,「?」表明未知類型,這個類型是實現Collection接口。那麼上面實現的方式能夠寫爲:
1
2
3
4
5
6
7
8
9
10
11
public  class  CollectionGenFooDemo {
     public  static  void  main(String args[]) {
         CollectionGenFoo<ArrayList> listFoo =  null ;
         listFoo =  new  CollectionGenFoo<ArrayList>( new  ArrayList());
         // 出錯了,不讓這麼幹。
         // CollectionGenFoo<Collection> listFoo1 = null;
         // listFoo1=new CollectionGenFoo<ArrayList>(new ArrayList());
         System.out.println( "實例化成功!" );
     }
}
注意:
一、若是隻指定了<?>,而沒有extends,則默認是容許Object及其下的任何Java類了。也就是任意類。
二、 通配符泛型不單能夠向下限制,如<? extends Collection>,還能夠向上限制,如<? super Double>,表示類型只能接受Double及其上層父類類型,如Number、Object類型的實例。
三、 泛型類定義能夠有多個泛型參數,中間用逗號隔開,還能夠定義泛型接口,泛型方法。這些都與泛型類中泛型的使用規則相似。

泛型方法

是否擁有泛型方法,與其所在的類是否泛型沒有關係。要定義泛型方法,只需將泛型參數列表置於返回值前。如:
1
2
3
4
5
6
7
8
9
10
11
12
13
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方法須要使用泛型能力,必須使其成爲泛型方法。
相關文章
相關標籤/搜索