如何在Java中使用泛型 -Manusha

banq發表於2020-05-23

泛型是Java中的關鍵概念。大多數Java程式碼庫都將使用它們。因此,不可避免的是某個時候開發人員會遇到它們。這就是為什麼正確理解它們至關重要。正確理解泛型也將幫助您獲得Java面試機會。
在本文中,我將討論泛型是什麼,如何在Java中使用它們以及它們的優點。

Java 5中新增了泛型,以提供編譯時型別檢查,並消除了使用集合類時常見的ClassCastException風險。
Java中的集合類用於儲存和操作物件組。例如,ArrayList集合類可以儲存任何型別的物件。因為它被設計為Java基類型別Object的容器。因此,ArrayList物件可以容納String或Integer或任何Java型別。使用泛型,我們可以定義ArrayList可以容納的物件型別。因此允許建立單個物件型別ArrayLists。

public class GenEx1{
     public static void main(String []args){
         ArrayList<String> al = new ArrayList<String>();
         al.add("Name");
         al.add("Age");
         al.add(22); // Compile Error!
     }
}


如您在上面的示例中看到的那樣,泛型型別是透過使用尖括號來定義的。在此示例中,只能將String物件儲存在ArrayList中。Java中的集合類現在具有通用型別。現在讓我們看看如何編寫我們自己的通用類,介面和方法。

泛型類
在泛型類宣告中,類的名稱後跟型別引數部分。我們可以遵循相同的語法來建立泛型介面。型別引數也被稱為型別變數,是用於指定一個泛型的型別名稱的識別符號。泛型類的型別引數部分可以包含一個或多個用逗號分隔的型別引數。這些類也稱為引數化類。

class Test<K, V>{
    private K key;
    private V value;
    
    Test(K key, V value){
        this.key = key;
        this.value = value;
    }
    
    public K getKey(){
        return key;
    }
    public V getValue(){
        return value;
    }
    
}
public class GenEx2{
     public static void main(String []args){
         Test<String,Integer> pair1 = new Test<String,Integer>("ID",223);
         Test<String,Integer> pair2 = new Test<String,Integer>("Age",22);
         System.out.println(pair1.getKey() + "=" + pair1.getValue() );
         System.out.println(pair2.getKey() + "=" + pair2.getValue() );
     }
}


在上面的示例中,Test類具有兩個名為K和V的型別引數。因此,Test類的物件可以儲存兩種不同型別的值。

泛型方法
如果我們可以編寫一個單一的排序方法來對Integer陣列,String陣列或任何支援排序的型別的陣列中的元素進行排序,那會很好,對嗎?Java泛型方法允許我們使用單個方法宣告來指定一組相關型別。您將能夠編寫一個可以用不同型別的引數呼叫的通用方法宣告。必須在方法返回型別之前指定型別引數部分。型別引數也可以用作方法的返回型別。

public class GenEx3{
    
     public static < E > void printArray( E[] inputArray ) {
        for(E element : inputArray) {
            System.out.print(element +" ");
        }
        System.out.println();
   }

     public static void main(String args[]){
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'P', 'I', 'Z', 'Z', 'A' };

        System.out.print("integerArray contains: ");
        printArray(intArray);
        System.out.print("doubleArray contains: ");
        printArray(doubleArray);
        System.out.print("characterArray contains: ");
        printArray(charArray);
     }
}

在上面的示例中,printArray方法可用於列印任何型別的陣列的元素。

泛型中的有界型別引數
到目前為止,我們只看到了無界限的泛型型別引數。無界意味著我們的泛型型別引數可以是我們想要的任何型別。有時您可能希望限制允許傳遞給引數的型別。例如,對數字進行操作的方法可能只希望接受Number類或其子類的例項。有界型別引數用於此目的。要宣告一個有界的型別引數,請列出該型別引數的名稱,然後是extends關鍵字,然後是其上限。

public abstract class Cage<T extends Animal> {
    abstract void addAnimal(T animal)
}

class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}


在示例中,籠型的通用型別必須始終是Animal或Animal類的子類。因此,我們可以將Cat,Dog或Animal類作為通用型別引數傳遞。
如果需要,我們還可以為泛型型別宣告多個範圍。因此,可以修改以下示例中的抽象類,如下所示。

public abstract class Cage<T extends Animal & Comparable<T>>

在這裡,型別引數現在必須同時考慮Animal類和Comparable介面。

泛型中的萬用字元和子型別
問號(?)是泛型中的萬用字元,表示未知型別。如果我們希望我們的通用方法適用於所有型別,在這種情況下,可以使用無界萬用字元。無界萬用字元由<?>表示。我們還可以使用有界萬用字元,有界萬用字元有兩種型別:上界萬用字元和下界萬用字元。
上界萬用字元用於在方法中放寬對變數型別的限制。例如,假設我們不知道列表將是數字,整數還是雙精度型別。那麼我們如何獲得該列表中元素的總和?我們可以使用上限萬用字元來解決此問題。下面的示例顯示瞭如何實現它。

public void method( List<? extends Number> list){
  double sum = 0;
  for(Number i : list){
   sum += i.doubleValue();
  }
  System.out.println(sum);
}


下界萬用字元用於增加方法中對變數型別的限制。假設我們只想將Integers新增到列表中,而我們也想接受Integer的超型別列表。我們可以使用下界萬用字元來實現此目的。從下面的示例中,我們可以看到如何使用它。

public void addIntegers(List<? super Integer> list){
 list.add(new Integer(10));
 list.add(new Integer(20));
}


雖然Integer在Java中是一個亞型Number,List<Integer>是不是一個亞型List<Number>?他們的共同父母是List<?>。因此,泛型類中的子型別使用萬用字元完成。下面的示例將幫助您理解這一點。

ArrayList<? extends Integer> intList = new ArrayList<>();
ArrayList<? extends Number>  numList = intList; // OK

ArrayList<Integer> intList2 = new ArrayList<>();
ArrayList<Number>  numList2 = intList2; // Compile Error!


泛型的優勢
既然您知道如何使用泛型,那麼您必須考慮為什麼我們需要使用它們。嗯,我們使用它們的三個主要原因是:

  1. 型別安全
  2. 不需要型別轉換
  3. 可重用程式碼

泛型確保了編譯時的安全性,這使您可以在編譯程式碼時捕獲無效型別。因此,您無需擔心執行時異常。不需要泛型轉換是使用泛型的另一個優點。您定義初始型別,然後讓程式碼為您進行轉換。我們還可以避免程式碼重複。沒有泛型,我們必須複製並貼上相同的程式碼,但要使用不同的型別。使用泛型,我們不必這樣做。

相關文章