Java泛型編程


1. 泛型類

  泛型類就是具有一個或者多個類型變量的類,在Java集合框架中大量使用了泛型類。通過泛型編程可以使編寫的代碼被很多不同的類型所共享,大大提高了代碼的重用性。

  下面給出一個自定義泛型類的例子:

public class Pair<T>
{
   private T first;
   private T second;
   
   public Pair(T first,T second)
  {
     this.first = first;
     this.second = second;
  }

  public T getFirst()
  { 
     return first;
  }
  
  public T getSecond()
  {
    return second; 
  }

 public void setFirst(T first)
 {
    this.first = first; 
 }
  
 public void setSecond(T second)
 { 
    this.second = second;
 }  
 
}

  使用普通的類名替換類型變量T就可以實例化泛型類型,如:Pair<String>,Java的泛型類類似於C++的模板類。

 

2. 泛型方法

 Java還可以定義帶有類型參數的方法,即泛型方法,泛型方法可以定義在泛型類中,也可以定義在普通類中。

public class ArrayHelper
{
    public static <T> T getMiddle(T[] array)
    {
        return array[array.length / 2];
    }
}

 上述的泛型方法,參數是泛型數組,返回值是泛型變量,在修飾符后面跟有<T>表示這是泛型方法。調用一個泛型方法時在方法名前的"<>"加入具體類型,如:ArrayHelper.<String>getMiddle(new String[]{"left","middle","right"}) ,其實大多數情況下也可以省略<String>。

 

3. 類型變量的限定

  有些時候,我們希望能使用不同的類型,但又希望這類型能滿足某些約束條件,這就要依靠對類型變量的限定。

public class ArrayHelper
{
  public static <T> T max(T[] array)
  { 
      T  max = array[0];
      for(int i = 1; i <  array.length; i++)
        if(array[i].compareTo(max) > 0)
         max = array[i];
return max; } }

 我們希望使用compareTo方法來比較泛型數組中的每個元素,從而選擇出最大的那個元素,而這就要求類型必須實現了Comparable接口,我們就可以對類型變量T作出如下限定:

public class ArrayHelper
{
  public static <T extends Comparable> T max(T[] array)
  { 
      T  max = array[0];
      for(int i = 1; i <  array.length; i++)
        if(array[i].compareTo(max) > 0)
         max = array[i];
return max; } }

 一個類型變量可以有多個限定,如: <T extends  Comparable & Serializable> 。

 

4. 類型擦除

 我們定義一個泛型類型后,就可以適配多種不同的類型,然而實際上虛擬機只知道一個原始類型,例如,對於上面定義個Pair<T>,其對應的原始類型如下:

public class Pair
{
   private Object first;
   private Object second;
   
   public Pair(Object first,Object second)
  {
     this.first = first;
     this.second = second;
  }

  public Object getFirst()
  { 
     return first;
  }
  
  public Object getSecond()
  {
    return second; 
  }

 public void setFirst(Object first)
 {
    this.first = first; 
 }
  
 public void setSecond(Object second)
 { 
    this.second = second;
 }  
 
}

 即將T替換成了Object類,實際上是將T替換成限定的類型。假設<T extends Comparable>,則就會將T替換成Comparable,如果有多個限定類型,則替換成第一個限定類型。如果沒有限定類型,就替換成Object類,這個過程即類型擦除。

 所以泛型類編譯成字節碼后就是一個普通的類。

 

5. 泛型類的繼承規則

(1)假設有一個print方法打印雇員對,參數是Pari<Employee>

public void print(Pair<Employee>)
{
   .....
}

      Manager類是Employee類的子類,那么Pair<Manager>是Pair<Employee>的子類么?,可以傳入print方法么?答案是不行,Pair<Manager>不是Pair<Employee>的子類。

(2) 永遠可以將參數化類型轉換成原始類型,如:Pair  pair = new Pair<Manager>("Jack","Mike"); 這是為了與泛型之前的遺留代碼能夠保持銜接。

(3)泛型類可以像普通類一樣繼承其他類,實現接口。如: class Pair<T>  implements Comparable 。

 

6. 通配符類型

 Pair<? extends Fruit> 表示任何泛型Pair類型,它的類型參數是Fruit的子類。Pair<Fruit>和Pair<Apple>都是Pair<? extends Fruit>的子類型。

 Pair<? super Apple>表示任何泛型Pair類型,它的類型參數是Apple的父類。Pair<Fruit>和Pair<Object>都是Pair<? super Apple>的子類型。

 這樣,就可以利用參數多態了,修改上面的print方法:

public void print(Pair<? extends Fruit>)
{
   .....
}

 現在就可以傳入Pair<Apple>和Pair<Banana>等作為參數了。

 但是對於通配符類型的多態,使用父類變量引用子類實例時,需要注意以下的問題:

Pair<Apple>  apples = new Pair<Apple>(new Apple("apple1"),new Apple("apple2"));
Pair<? extends Fruit>  fruits = apples;
//下面兩句調用setFirst方法編譯報錯,因為編譯器只知道Pair中保存類型的是Fruit的子類,但不知道具體是什么類型。
fruits.setFirst(new Apple("apple3"));    
fruits.setFirst(new Banana("banana1"));
//下面調用getFirst方法不會出錯,因為編譯器知道Pair中保存的類型一定是Fruit的子類,轉換成Fruit類不會出錯。
Fruit  first = frutis.getFirst();

 即對於<? extends Type> 通配符類型,使用父類變量引用子類實例時,不能對子類實例進行寫,只能讀。

Pair<Apple>  apples = new Pair<Apple>(new Apple("apple1"),new Apple("apple2"));
Pair<? super Apple>  fruits = apples;
fruits.setFirst(new GoodApple("apple3"));  //這一句調用setFirst方法不會出錯,因為編譯器知道Pair中保存的類型一定是Apple類的父類,因此,傳入Apple類對象或者Apple類的子類對象都是可以的
fruits.setFirst(new Fruit("banana1")); //傳入Apple類的父類對象就會編譯錯誤
Fruit first = frutis.getFirst(); //編譯不通過,因為編譯器知道Pair中保存的類型是Apple類的父類,但不知道具體是什么類,因此,只能賦值給Object類的變量

即對於<? super Type> 通配符類型,使用父類變量引用子類實例時,不能對子類實例進行讀,只能寫。

 

參考資料 《Java核心技術》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM