泛型簡析 - JDK 1.5

[原文鏈接 ] (https://blog.csdn.net/qq_27093465/article/details/73229016)

一、引入

1、泛型是什麼

首先告訴大家ArrayList就是泛型。那ArrayList能完成哪些想不到的功能呢?先看看下面這段代碼:

[java] view plain copy


  1. ArrayList<String> strList = new ArrayList<String>();  
  2. ArrayList<Integer> intList = new ArrayList<Integer>();  
  3. ArrayList<Double> doubleList = new ArrayList<Double>();  


大家對ArrayList很熟悉,這裏構造了三個List,分別盛裝String、Integer和Double;這就是ArrayList的過人之處:即各種類型的變量都可以組裝成對應的List,而不必針對每個類型分別實現一個構建ArrayList的類。這裏可能看不懂,開篇總是困難的,下面看看如果沒有泛型的話,我們要怎麼做;

2、沒有泛型會怎樣

先看下面這段代碼:

我們實現兩個能夠設置點座標的類,分別設置Integer類型的點座標和Float類型的點座標:

[java] view plain copy


  1. //設置Integer類型的點座標  
  2. class IntegerPoint{  
  3.     private Integer x ;       // 表示X座標  
  4.     private Integer y ;       // 表示Y座標  
  5.     public void setX(Integer x){  
  6.         this.x = x ;  
  7.     }  
  8.     public void setY(Integer y){  
  9.         this.y = y ;  
  10.     }  
  11.     public Integer getX(){  
  12.         return this.x ;  
  13.     }  
  14.     public Integer getY(){  
  15.         return this.y ;  
  16.     }  
  17. }  
  18. //設置Float類型的點座標  
  19. class FloatPoint{  
  20.     private Float x ;       // 表示X座標  
  21.     private Float y ;       // 表示Y座標  
  22.     public void setX(Float x){  
  23.         this.x = x ;  
  24.     }  
  25.     public void setY(Float y){  
  26.         this.y = y ;  
  27.     }  
  28.     public Float getX(){  
  29.         return this.x ;  
  30.     }  
  31.     public Float getY(){  
  32.         return this.y ;  
  33.     }  
  34. }  


那現在有個問題:大家有沒有發現,他們除了變量類型不一樣,一個是Integer一個是Float以外,其它並沒有什麼區別!那我們能不能合併成一個呢?

答案是可以的,因爲Integer和Float都是派生自Object的,我們用下面這段代碼代替:
[java] view plain copy


  1. class ObjectPoint{  
  2.     private Object x ;  
  3.     private Object y ;  
  4.     public void setX(Object x){  
  5.         this.x = x ;  
  6.     }  
  7.     public void setY(Object y){  
  8.         this.y = y ;  
  9.     }  
  10.     public Object getX(){  
  11.         return this.x ;  
  12.     }  
  13.     public Object getY(){  
  14.         return this.y ;  
  15.     }  
  16. }  


即全部都用Object來代替所有的子類;

在使用的時候是這樣的:
[java] view plain copy


  1. ObjectPoint integerPoint = new ObjectPoint();  
  2. integerPoint.setX(new Integer(100));  
  3. Integer integerX=(Integer)integerPoint.getX();  


在設置的時候,使用new Integer(100)來新建一個Integer
[java] view plain copy


  1. integerPoint.setX(new Integer(100));  


然後在取值的時候,進行強制轉換:
[java] view plain copy


  1. Integer integerX=(Integer)integerPoint.getX();  


由於我們設置的時候,是設置的Integer,所以在取值的時候,強制轉換是不會出錯的。

同理,FloatPoint的設置和取值也是類似的,代碼如下:
[java] view plain copy


  1. ObjectPoint floatPoint = new ObjectPoint();  
  2. floatPoint.setX(new Float(100.12f));  
  3. Float floatX = (Float)floatPoint.getX();  


但問題來了:注意,注意,我們這裏使用了強制轉換,我們這裏setX()和getX()寫得很近,所以我們明確的知道我們傳進去的是Float類型,那如果我們記錯了呢?

比如我們改成下面這樣,編譯時會報錯嗎:
[java] view plain copy


  1. ObjectPoint floatPoint = new ObjectPoint();  
  2. floatPoint.setX(new Float(100.12f));  
  3. String floatX = (String)floatPoint.getX();  


不會!!!我們問題的關鍵在於這句:
[java] view plain copy


  1. String floatX = (String)floatPoint.getX();  


強制轉換時,會不會出錯。因爲編譯器也不知道你傳進去的是什麼,而floatPoint.getX()返回的類型是Object,所以編譯時,將Object強轉成String是成立的。必然不會報錯。

而在運行時,則不然,在運行時,floatPoint實例中明明傳進去的是Float類型的變量,非要把它強轉成String類型,肯定會報類型轉換錯誤的!

那有沒有一種辦法在編譯階段,即能合併成同一個,又能在編譯時檢查出來傳進去類型不對呢?當然,這就是泛型。

下面我們將對泛型的寫法和用法做一一講解。

二、各種泛型定義及使用

1、泛型類定義及使用

我們先看看泛型的類是怎麼定義的:

[java] view plain copy
  1. //定義  
  2. class Point<T>{// 此處可以隨便寫標識符號   
  3.     private T x ;        
  4.     private T y ;        
  5.     public void setX(T x){//作爲參數  
  6.         this.x = x ;  
  7.     }  
  8.     public void setY(T y){  
  9.         this.y = y ;  
  10.     }  
  11.     public T getX(){//作爲返回值  
  12.         return this.x ;  
  13.     }  
  14.     public T getY(){  
  15.         return this.y ;  
  16.     }  
  17. };  
  18. //IntegerPoint使用  
  19. Point<Integer> p = new Point<Integer>() ;   
  20. p.setX(new Integer(100)) ;   
  21. System.out.println(p.getX());    
  22.   
  23. //FloatPoint使用  
  24. Point<Float> p = new Point<Float>() ;   
  25. p.setX(new Float(100.12f)) ;   
  26. System.out.println(p.getX());    
先看看運行結果:

從結果中可以看到,我們實現了開篇中IntegerPoint類和FloatPoint類的效果。下面來看看泛型是怎麼定義及使用的吧。

(1)、定義泛型:Point<T>
首先,大家可以看到Point<T>,即在類名後面加一個尖括號,括號裏是一個大寫字母。這裏寫的是T,其實這個字母可以是任何大寫字母,大家這裏先記着,可以是任何大寫字母,意義是相同的。
(2)類中使用泛型
這個T表示派生自Object類的任何類,比如String,Integer,Double等等。這裏要注意的是,T一定是派生於Object類的。爲方便起見,大家可以在這裏把T當成String,即String在類中怎麼用,那T在類中就可以怎麼用!所以下面的:定義變量,作爲返回值,作爲參數傳入的定義就很容易理解了。

[java] view plain copy
  1. //定義變量  
  2. private T x ;   
  3. //作爲返回值  
  4. public T getX(){   
  5.     return x ;    
  6. }    
  7. //作爲參數  
  8. public void setX(T x){    
  9.     this.x = x ;    
  10. }   
(3)使用泛型類
下面是泛型類的用法:
[java] view plain copy
  1. //IntegerPoint使用  
  2. Point<Integer> p = new Point<Integer>() ;   
  3. p.setX(new Integer(100)) ;   
  4. System.out.println(p.getX());    
  5.   
  6. //FloatPoint使用  
  7. Point<Float> p = new Point<Float>() ;   
  8. p.setX(new Float(100.12f)) ;   
  9. System.out.println(p.getX());    
首先,是構造一個實例:
[java] view plain copy
  1. Point<String> p = new Point<String>() ;   
這裏與普通構造類實例的不同之點在於,普通類構造函數是這樣的:Point p = new Point() ; 
而泛型類的構造則需要在類名後添加上<String>,即一對尖括號,中間寫上要傳入的類型。
因爲我們構造時,是這樣的:class Point<T>,所以在使用的時候也要在Point後加上類型來定義T代表的意義。
然後在getVar()和setVar()時就沒有什麼特殊的了,直接調用即可。
從上面的使用時,明顯可以看出泛型的作用,在構造泛型類的實例的時候:
[java] view plain copy
  1. //IntegerPoint使用  
  2. Point<Integer> p = new Point<Integer>() ;   
  3. //FloatPoint使用  
  4. Point<Float> p = new Point<Float>() ;   
尖括號中,你傳進去的是什麼,T就代表什麼類型。這就是泛型的最大作用,我們只需要考慮邏輯實現,就能拿給各種類來用。
前面我們提到ArrayList也是泛型,我們順便它的實現:
[java] view plain copy
  1. public class ArrayList<E>{  
  2.     …………  
  3. }  
看到了吧,跟我們的Point實現是一樣的,這也就是爲什麼ArrayList能夠盛裝各種類型的主要原因。
(4)使用泛型實現的優勢
相比我們開篇時使用Object的方式,有兩個優點:
(1)、不用強制轉換

[java] view plain copy
  1. //使用Object作爲返回值,要強制轉換成指定類型  
  2. Float floatX = (Float)floatPoint.getX();  
  3. //使用泛型時,不用強制轉換,直接出來就是String  
  4. System.out.println(p.getVar());   
(2)、在settVar()時如果傳入類型不對,編譯時會報錯

可以看到,當我們構造時使用的是String,而在setVar時,傳進去Integer類型時,就會報錯。而不是像Object實現方式一樣,在運行時纔會報強制轉換錯誤。

2、多泛型變量定義及字母規範

(1)、多泛型變量定義
上在我們只定義了一個泛型變量T,那如果我們需要傳進去多個泛型要怎麼辦呢?
只需要在類似下面這樣就可以了:

[java] view plain copy
  1. class MorePoint<T,U>{  
  2. }  
也就是在原來的T後面用逗號隔開,寫上其它的任意大寫字母即可。想加幾個就加幾個,比如我們想加五個泛型變量,那應該是這樣的:

[java] view plain copy
  1. class MorePoint<T,U,A,B,C>{  
  2. }  
舉個粟子,我們在Point上再另加一個字段name,也用泛型來表示,那要怎麼做?代碼如下:
[java] view plain copy
  1. class MorePoint<T,U> {  
  2.     private T x;  
  3.     private T y;         
  4.   
  5.     private U name;  
  6.   
  7.     public void setX(T x) {  
  8.         this.x = x;  
  9.     }  
  10.     public T getX() {  
  11.         return this.x;  
  12.     }  
  13.     …………  
  14.     public void setName(U name){  
  15.         this.name = name;  
  16.     }  
  17.   
  18.     public U getName() {  
  19.         return this.name;  
  20.     }  
  21. }  
  22. //使用  
  23. MorePoint<Integer,String> morePoint = new MorePoint<Integer, String>();  
  24. morePoint.setName(”harvic”);  
  25. Log.d(TAG, ”morPont.getName:” + morePoint.getName());  
從上面的代碼中,可以明顯看出,就是在新添加的泛型變量U用法與T是一樣的。
(2)、字母規範
在定義泛型類時,我們已經提到用於指定泛型的變量是一個大寫字母:
[java] view plain copy
  1. class Point<T>{  
  2.  …………  
  3. }  
當然不是的!!!!任意一個大寫字母都可以。他們的意義是完全相同的,但爲了提高可讀性,大家還是用有意義的字母比較好,一般來講,在不同的情境下使用的字母意義如下:
  •  E — Element,常用在java Collection裏,如:List<E>,Iterator<E>,Set<E>
  •  K,V — Key,Value,代表Map的鍵值對
  •  N — Number,數字
  •  T — Type,類型,如String,Integer等等
如果這些還不夠用,那就自己隨便取吧,反正26個英文字母呢。
再重複一遍,使用哪個字母是沒有特定意義的!只是爲了提高可讀性!!!!

3、泛型接口定義及使用

在接口上定義泛型與在類中定義泛型是一樣的,代碼如下:

[java] view plain copy
  1. interface Info<T>{        // 在接口上定義泛型    
  2.     public T getVar() ; // 定義抽象方法,抽象方法的返回值就是泛型類型    
  3.     public void setVar(T x);  
  4. }    

與泛型類的定義一樣,也是在接口名後加尖括號;
(1)、使用方法一:非泛型類
但是在使用的時候,就出現問題了,我們先看看下面這個使用方法:

[java] view plain copy
  1. class InfoImpl implements Info<String>{   // 定義泛型接口的子類  
  2.     private String var ;                // 定義屬性  
  3.     public InfoImpl(String var){        // 通過構造方法設置屬性內容  
  4.         this.setVar(var) ;  
  5.     }  
  6.     @Override  
  7.     public void setVar(String var){  
  8.         this.var = var ;  
  9.     }  
  10.     @Override  
  11.     public String getVar(){  
  12.         return this.var ;  
  13.     }  
  14. }  
  15.   
  16. public class GenericsDemo24{  
  17.     public  void main(String arsg[]){  
  18.         InfoImpl i = new InfoImpl(“harvic”);  
  19.         System.out.println(i.getVar()) ;  
  20.     }  
  21. };  
首先,先看InfoImpl的定義:
[java] view plain copy
  1. class InfoImpl implements Info<String>{     
  2.  …………  
  3. }  
要清楚的一點是InfoImpl不是一個泛型類!因爲他類名後沒有<T>!
然後在在這裏我們將Info<String>中的泛型變量T定義填充爲了String類型。所以在重寫時setVar()和getVar()時,IDE會也我們直接生成String類型的重寫函數。
最後在使用時,沒什麼難度,傳進去String類型的字符串來構造InfoImpl實例,然後調用它的函數即可。
[java] view plain copy
  1. public class GenericsDemo24{  
  2.     public  void main(String arsg[]){  
  3.         InfoImpl i = new InfoImpl(“harvic”);  
  4.         System.out.println(i.getVar()) ;  
  5.     }  
  6. };  
(2)、使用方法二:泛型類

在方法一中,我們在類中直接把Info<T>接口給填充好了,但我們的類,是可以構造成泛型類的,那我們利用泛型類來構造填充泛型接口會是怎樣呢?

[java] view plain copy
  1. interface Info<T>{        // 在接口上定義泛型  
  2.     public T getVar() ; // 定義抽象方法,抽象方法的返回值就是泛型類型  
  3.     public void setVar(T var);  
  4. }  
  5. class InfoImpl<T> implements Info<T>{   // 定義泛型接口的子類  
  6.     private T var ;             // 定義屬性  
  7.     public InfoImpl(T var){     // 通過構造方法設置屬性內容  
  8.         this.setVar(var) ;    
  9.     }  
  10.     public void setVar(T var){  
  11.         this.var = var ;  
  12.     }  
  13.     public T getVar(){  
  14.         return this.var ;  
  15.     }  
  16. }  
  17. public class GenericsDemo24{  
  18.     public static void main(String arsg[]){  
  19.         InfoImpl<String> i = new InfoImpl<String>(“harvic”);  
  20.         System.out.println(i.getVar()) ;  
  21.     }  
  22. };  

最關鍵的是構造泛型類的過程:
[java] view plain copy
  1. class InfoImpl<T> implements Info<T>{   // 定義泛型接口的子類  
  2.     private T var ;             // 定義屬性  
  3.     public InfoImpl(T var){     // 通過構造方法設置屬性內容  
  4.         this.setVar(var) ;    
  5.     }  
  6.     public void setVar(T var){  
  7.         this.var = var ;  
  8.     }  
  9.     public T getVar(){  
  10.         return this.var ;  
  11.     }  
  12. }  
在這個類中,我們構造了一個泛型類InfoImpl<T>,然後把泛型變量T傳給了Info<T>,這說明接口和泛型類使用的都是同一個泛型變量。
然後在使用時,就是構造一個泛型類的實例的過程,使用過程也不變。
[java] view plain copy
  1. public class GenericsDemo24{  
  2.     public static void main(String arsg[]){  
  3.         Info<String> i = new InfoImpl<String>(“harvic”);  
  4.         System.out.println(i.getVar()) ;  
  5.     }  
  6. };  
使用泛型類來繼承泛型接口的作用就是讓用戶來定義接口所使用的變量類型,而不是像方法一那樣,在類中寫死。
那我們稍微加深點難度,構造一個多個泛型變量的類,並繼承自Info接口:
[java] view plain copy
  1. class InfoImpl<T,K,U> implements Info<U>{   // 定義泛型接口的子類  
  2.      private U var ;      
  3.      private T x;  
  4.      private K y;  
  5.      public InfoImpl(U var){        // 通過構造方法設置屬性內容  
  6.          this.setVar(var) ;  
  7.      }  
  8.      public void setVar(U var){  
  9.          this.var = var ;  
  10.      }  
  11.      public U getVar(){  
  12.          return this.var ;  
  13.      }  
  14.  }  
在這個例子中,我們在泛型類中定義三個泛型變量T,K,U並且把第三個泛型變量U用來填充接口Info。所以在這個例子中Info所使用的類型就是由U來決定的。
使用時是這樣的:泛型類的基本用法,不再多講,代碼如下:
[java] view plain copy
  1. public class GenericsDemo24{  
  2.     public  void main(String arsg[]){  
  3.         InfoImpl<Integer,Double,String> i = new InfoImpl<Integer,Double,String>(“harvic”);  
  4.         System.out.println(i.getVar()) ;  
  5.     }  
  6. }  

4、泛型函數定義及使用

上面我們講解了類和接口的泛型使用,下面我們再說說,怎麼單獨在一個函數裏使用泛型。比如我們在新建一個普通的類StaticFans,然後在其中定義了兩個泛型函數:
[java] view plain copy
  1. public class StaticFans {  
  2.     //靜態函數  
  3.     public static  <T> void StaticMethod(T a){  
  4.         Log.d(”harvic”,“StaticMethod: ”+a.toString());  
  5.     }  
  6.     //普通函數  
  7.     public  <T> void OtherMethod(T a){  
  8.         Log.d(”harvic”,“OtherMethod: ”+a.toString());  
  9.     }  
  10. }  
上面分別是靜態泛型函數和常規泛型函數的定義方法,與以往方法的唯一不同點就是在返回值前加上<T>來表示泛型變量。其它沒什麼區別。
使用方法如下:
[java] view plain copy
  1. //靜態方法  
  2. StaticFans.StaticMethod(”adfdsa”);//使用方法一  
  3. StaticFans.<String>StaticMethod(”adfdsa”);//使用方法二  
  4.   
  5. //常規方法  
  6. StaticFans staticFans = new StaticFans();  
  7. staticFans.OtherMethod(new Integer(123));//使用方法一  
  8. staticFans.<Integer>OtherMethod(new Integer(123));//使用方法二  
結果如下:

首先,我們看靜態泛型函數的使用方法:

[java] view plain copy
  1. StaticFans.StaticMethod(“adfdsa”);//使用方法一  
  2. StaticFans.<String>StaticMethod(”adfdsa”);//使用方法二  
從結果中我們可以看到,這兩種方法的結果是完全一樣的,但他們還有些區別的,區別如下:
方法一,可以像普通方法一樣,直接傳值,任何值都可以(但必須是派生自Object類的類型,比如String,Integer等),函數會在內部根據傳進去的參數來識別當前T的類別。但儘量不要使用這種隱式的傳遞方式,代碼不利於閱讀和維護。因爲從外觀根本看不出來你調用的是一個泛型函數。
方法二,與方法一不同的地方在於,在調用方法前加了一個<String>來指定傳給<T>的值,如果加了這個<String>來指定參數的值的話,那StaticMethod()函數裏所有用到的T類型也就是強制指定了是String類型。這是我們建議使用的方式。
同樣,常規泛型函數的使用也有這兩種方式:
[java] view plain copy
  1. StaticFans staticFans = new StaticFans();  
  2. staticFans.OtherMethod(new Integer(123));//使用方法一  
  3. staticFans.<Integer>OtherMethod(new Integer(123));//使用方法二  
可以看到,與平常一樣,先創建類的實例,然後調用泛型函數。
方法一,隱式傳遞了T的類型,與上面一樣,不建議這麼做。
方法二,顯示將T賦值爲Integer類型,這樣OtherMethod(T a)傳遞過來的參數如果不是Integer那麼編譯器就會報錯。

進階:返回值中存在泛型
上面我們的函數中,返回值都是void,但現實中不可能都是void,有時,我們需要將泛型變量返回,比如下面這個函數:
[java] view plain copy
  1. public static <T> List<T> parseArray(String response,Class<T> object){  
  2.     List<T> modelList = JSON.parseArray(response, object);  
  3.     return modelList;  
  4. }  
函數返回值是List<T>類型。至於傳入參數Class<T> object的意義,我們下面會講。這裏也就是想通過這個例子來告訴大家,泛型變量其實跟String,Integer,Double等等的類的使用上沒有任何區別,T只是一個符號,可以代表String,Integer,Double……這些類的符號,在泛型函數使用時,直接把T看到String,Integer,Double……中的任一個來寫代碼就可以了。唯一不同的是,要在函數定義的中在返回值前加上<T>標識泛型;

5、其它用法:Class<T>類傳遞及泛型數組

(1)、使用Class<T>傳遞泛型類Class對象
有時,我們會遇到一個情況,比如,我們在使用JSON解析字符串的時候,代碼一般是這樣的

[java] view plain copy
  1. public static List<SuccessModel> parseArray(String response){  
  2.     List<SuccessModel> modelList = JSON.parseArray(response, SuccessModel.class);  
  3.     return modelList;  
  4. }  
其中SuccessModel是自定義的解析類,代碼如下,其實大家不用管SuccessModel的定義,只考慮上面的那段代碼就行了。寫出來SuccessModel的代碼,只是不想大家感到迷惑,其實,這裏只是fastJson的基本用法而已。
這段代碼的意義就是根據SuccessModel解析出List<SuccessModel>的數組。
[java] view plain copy
  1. public class SuccessModel {  
  2.     private boolean success;  
  3.       
  4.     public boolean isSuccess() {  
  5.         return success;  
  6.     }  
  7.   
  8.     public void setSuccess(boolean success) {  
  9.         this.success = success;  
  10.     }  
  11. }   
那現在,我們把下面這句組裝成一個泛型函數要怎麼來做呢?
[java] view plain copy
  1. public static List<SuccessModel> parseArray(String response){  
  2.     List<SuccessModel> modelList = JSON.parseArray(response, SuccessModel.class);  
  3.     return modelList;  
  4. }  
首先,我們應該把SuccessModel單獨抽出來做爲泛型變量,但parseArray()中用到的SuccessModel.class要怎麼弄呢?
先來看代碼:
[java] view plain copy
  1. public static <T> List<T> parseArray(String response,Class<T> object){  
  2.     List<T> modelList = JSON.parseArray(response, object);  
  3.     return modelList;  
  4. }  
注意到,我們用的Class<T> object來傳遞類的class對象,即我們上面提到的SuccessModel.class。
這是因爲Class<T>也是一泛型,它是傳來用來裝載類的class對象的,它的定義如下:
[java] view plain copy
  1. public final class Class<T> implements Serializable {  
  2.     …………  
  3. }  
通過Class<T>來加載泛型的Class對象的問題就講完了,下面來看看泛型數組的使用方法吧。
(2)、定義泛型數組
在寫程序時,大家可能會遇到類似String[] list = new String[8];的需求,這裏可以定義String數組,當然我們也可以定義泛型數組,泛型數組的定義方法爲 T[],與String[]是一致的,下面看看用法:

[java] view plain copy
  1. //定義  
  2. public static <T> T[] fun1(T…arg){  // 接收可變參數    
  3.        return arg ;            // 返回泛型數組    
  4. }    
  5. //使用  
  6. public static void main(String args[]){    
  7.        Integer i[] = fun1(1,2,3,4,5,6) ;  
  8.        Integer[] result = fun1(i) ;  
  9. }    
我們先看看 定義時的代碼:
[java] view plain copy
  1. public static <T> T[] fun1(T…arg){  // 接收可變參數    
  2.        return arg ;            // 返回泛型數組    
  3. }    
首先,定義了一個靜態函數,然後定義返回值爲T[],參數爲接收的T類型的可變長參數。如果有同學對T…arg的用法不瞭解,可以去找下JAVA 可變長參數方面的知識。
由於可變長參數在輸入後,會保存在arg這個數組中,所以,我們直接把數組返回即可。


轉自:http://blog.csdn.net/harvic880925/article/details/49872903


下面是我自己實際使用泛型的幾個實例。

希望看完上面的文章之後,再看實際使用的例子,可以更好的理解和使用和實際使用。


關於泛型類的使用實例

  1. import lombok.Data;  
  2.   
  3. @Data  
  4. public class MultiObject<T> {  
  5.   
  6.     /** 
  7.      * 成功狀態 
  8.      */  
  9.     private boolean success;  
  10.   
  11.     /** 
  12.      * 異常 
  13.      */  
  14.     private Exception ex;  
  15.   
  16.     /** 
  17.      * 數據 
  18.      */  
  19.     private T obj;  
  20.       
  21.     public MultiObject() {  
  22.     }  
  23.   
  24.     /** 
  25.      * 注意:當傳入的泛型是Boolean時,就和第三個構造函數衝突了。 
  26.      */  
  27.     public MultiObject(boolean success) {  
  28.         this.success = success;  
  29.     }  
  30.       
  31.     public MultiObject(Exception ex) {  
  32.         this.success = false;  
  33.         this.ex = ex;  
  34.     }  
  35.       
  36.     public MultiObject(T value) {  
  37.         this.success = true;  
  38.         this.obj = value;  
  39.     }  
  40. }  
import lombok.Data;

@Data
public class MultiObject<T> {

    /**
     * 成功狀態
     */
    private boolean success;

    /**
     * 異常
     */
    private Exception ex;

    /**
     * 數據
     */
    private T obj;

    public MultiObject() {
    }

    /**
     * 注意:當傳入的泛型是Boolean時,就和第三個構造函數衝突了。
     */
    public MultiObject(boolean success) {
        this.success = success;
    }

    public MultiObject(Exception ex) {
        this.success = false;
        this.ex = ex;
    }

    public MultiObject(T value) {
        this.success = true;
        this.obj = value;
    }
}

簡單解釋下這個model。
在實際業務代碼裏面,可能有很多種操作,然後我們關心這個操作的執行結果,主要有幾點。
1,成功與否。對應屬性success。
2,異常信息。對應屬性ex。若是操作正常執行,則就不在意這個屬性的值。
3,我們操作的最終目的對象。對應屬性obj。


泛型牛逼的地方就是在這個地方。
如果你不用泛型,而使用Object類型,那麼每次執行完之後,我們即使得到這個結果,還得類型轉換一下,那麼這下就像文章上面描述的那樣。分分鐘出現castfailexception。也就是類型轉換異常啦。
但是,若是使用了這個泛型之後,那麼我們的某個操作所需要的返回結果是什麼類型,就可以 傳入什麼類型,而且在實際取得返回結果的時候,就不需要使用類型轉換,這樣就很好的達到了目的。
這個主要是代碼設計層次的提高。寫再多的業務代碼,要是不提高,那麼寫的都是渣。

關於,這個model代碼裏面爲啥沒有getter和setter,都是因爲使用@Data這個註解,可以自動填充這個getter和setter。所以。就表在意這個問題啦。在其他地方可以正常使用各個屬性getter和setter方法,雖然這些方法,你暫時看不見。有興趣的可以瞭解下lombok。


關於泛型方法的使用實例

這個地方就有2個,但是上面文章也都講到啦。
1,一個是泛型表示某一個類型的參數。爲的傳遞某一類的參數對象
2,另一個則是傳遞的不是參數,而是代表Class,某一個類。

恰巧我都使用過,就正好記錄一下實際使用實例。

  1. /** 
  2.  * 將Json字符串信息轉換成對應的Java對象 
  3.  * 
  4.  * @param json json字符串對象 
  5.  * @param c    對應的類型 
  6.  */  
  7. public static <T> T parseJsonToObj(String json, Class<T> c) {  
  8.     try {  
  9.         JSONObject jsonObject = JSONObject.parseObject(json);  
  10.         return JSON.toJavaObject(jsonObject, c);  
  11.     } catch (Exception e) {  
  12.         LOG.error(e.getMessage());  
  13.     }  
  14.     return null;  
  15. }  
    /**
     * 將Json字符串信息轉換成對應的Java對象
     *
     * @param json json字符串對象
     * @param c    對應的類型
     */
    public static <T> T parseJsonToObj(String json, Class<T> c) {
        try {
            JSONObject jsonObject = JSONObject.parseObject(json);
            return JSON.toJavaObject(jsonObject, c);
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
        return null;
    }
然後是具體調用的地方的代碼。

  1. Collector collectorObj = JSONUtils.parseJsonToObj(collector, Collector.class);  
  2. Flume flume = JSONUtils.parseJsonToObj(flumeJson, Flume.class);  
  3. Probe probe = JSONUtils.parseJsonToObj(probeJson, Probe.class);  
Collector collectorObj = JSONUtils.parseJsonToObj(collector, Collector.class);
Flume flume = JSONUtils.parseJsonToObj(flumeJson, Flume.class);
Probe probe = JSONUtils.parseJsonToObj(probeJson, Probe.class);
可以看到,真的只是因爲傳入的參數類型不一樣,但若你不知道泛型的話,那你就得沒遇到一個類型的轉換,你就得寫一個這麼個方法。


  1. /** 
  2.  * @param dest   目的集合 
  3.  * @param source 源集合 
  4.  * @param <T>    集合參數的類型 
  5.  */  
  6. private static <T> void listAddAllAvoidNPE(List<T> dest, List<T> source) {  
  7.     if (source == null) {  
  8.         return;  
  9.     }  
  10.     dest.addAll(source);  
  11. }  
  12.   
  13. private static <T> void listAddAvoidNull(List<T> dest, T source) {  
  14.     if (source == null) {  
  15.         return;  
  16.     }  
  17.     dest.add(source);  
  18. }  
    /**
     * @param dest   目的集合
     * @param source 源集合
     * @param <T>    集合參數的類型
     */
    private static <T> void listAddAllAvoidNPE(List<T> dest, List<T> source) {
        if (source == null) {
            return;
        }
        dest.addAll(source);
    }

    private static <T> void listAddAvoidNull(List<T> dest, T source) {
        if (source == null) {
            return;
        }
        dest.add(source);
    }

這個就是傳入的參數爲某一類的參數,主要是要使用參數對象,而不是上面的那個使用的參數的類Class

我這方法提出來,主要是因爲,直接使用list類的addAll()方法,如果添加的是null,那麼就會拋異常。但是總不能我在所有使用的地方,都判斷一下我要添加的參數是不是null,然後再調用list的addAll()方法吧。那樣的話,這樣的判斷代碼會囉嗦的海了去了。所以,就這麼提出來了。

這個時候,這個T,使用起來就像使用我們常用的一般對象一樣,我這的參數是個List類型,當然也可是其他類型的,姿勢都一樣。

然後是具體調用的地方的代碼

  1. List<ProbeObject> list = Lists.newArrayList();  
  2. listAddAllAvoidNPE(list, decoder.getProperties());  
    List<ProbeObject> list = Lists.newArrayList();
    listAddAllAvoidNPE(list, decoder.getProperties());
這個方法的第二個參數的返回值可能是null,所以,直接調用addAll(),就會拋空指針異常。所以,就如上,那麼一提取。就好多啦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章