Java基礎知識之泛型全接觸

當我們在定義類,接口和方法時,可以接收一個類型作爲參數,這就叫做泛型

函數可以傳入普通的參數,也可以傳入一個類型參數。不同之處是普通的參數就是值而已,但是類型參數卻是個類型。

使用泛型的好處:

  • 強類型檢查。在編譯時就可以得到類型錯誤信息。
  • 避免顯式強制轉換。
  • 方便實現通用算法。

對類使用泛型

我們可以創建一個簡單的Class Box。它提供存取一個類型爲Object的對象。

public class Box {
    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    private Object object;

你可以傳給它任何你想要的對象,比如對象String,Integer等,也可以傳入自定義的一些對象。但是調用getObject方法返回的對象需要顯式的強轉爲傳入的類型,才能使用原來類型的一些方法。

我們可以使用泛型來構造這個對象。

public class Box<T> {
    public T getObject() {
        return object;
    }

    private T object;

    public void setObject(T object) {
        this.object = object;
    }
}

我們可以看到,所有的Object被替換成了T。T代表了某種類型,你在實例化Box對象時,必須要給其指定一種類型,String,Integer或者自定義的類,並且調用getObject方法並不需要進行強轉就可以使用該類型的方法。

一般來說,類型參數名稱越簡單越好,並且需要是大寫的。爲了方便,我們約定了一些命名使用。

  • E Element
  • K key
  • N Number
  • T type
  • V value
  • S,U,V 第2,3,4個類型

我們可以這樣實例化一個Box類。

Box<Integer> integerBox = new Box<Integer>();

同樣,我們也支持在一個類中傳入多個類型參數。例如下面的Pair對象

public class Pair<T, V> {
    private T key;
    private V value;

    public Pair(T key, V value) {
        this.key = key;
        this.value = value;
    }

    public T getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

使用方法如下。

Pair<Integer, String> one = new Pair<Integer, String>(1, "one");

Pair<String, String> hello = new Pair<String, String>("hello", "world");

對方法使用泛型

泛型可以作用與方法上,此時泛型參數只能在方法體中使用。而泛型作用於類時,則在整個類中可以使用。

在靜態方法、非靜態方法及構造函數都可以使用泛型。

public class Util {

    public static <T, U> boolean compare(Pair<T,U> pair1, Pair<T,U> pair2)
    {
        return pair1.getKey().equals(pair2.getKey()) && pair1.getValue().equals(pair2.getValue());
    }
}

下面是對該靜態方法的使用。

 Pair one = new Pair("one", 1);
        Pair two = new Pair("two", 2);

        assertThat(Util.compare(one, two), is(false));
// pass

對泛型進行限定

默認情況下如果直接使用的話,我們可以給其傳任何值。有時候我們想值允許傳入某個類及它的子類。這時候在聲明泛型時可以使用extends關鍵字。

public class Box<T extends Number> {
    public T getObject() {
        return object;
    }

    private T object;

    public void setObject(T object) {
        this.object = object;
    }
}
   Box box = new Box();
        box.setObject(10);    //ok
        box.setObject("hello");  //compile-time error

我們也可以給類型參數加多個限定。

<T extends B1 & B2 & B3>

加上限定類或接口以後,我們可以使用泛型參數變量調用該類或接口的方法。

通配符的使用

Java中的List就是一個實現了泛型的類,假如我們寫了一個方法,獲取List中元素的個數。只不過這個方法限定T類型爲Number。

public static int getCount(List<Number> list)
  {
      int i = 0;
      for(Number n : list)
      {
          i++;
      }
      return i;
  }

然後我們這樣試圖調用它。

 List<Integer> list = new ArrayList<Integer>(){
        {
            add(1);
            add(2);
            add(3);
        }
    };

    Util.getCount(list); //compile-time error

爲什麼會產生錯誤那?因爲我們要求方法的參數是List,而我們實際傳入的是List。雖然Integer是Number的子類,但是List卻不是List的子類,他們其實是平等的關係。這點一定要注意。我們在方法定義時已經明確表示T的類型是Number了,所以只能接收List,而不能接收其它類型的參數。 這時候?通配符就起作用了。我們可以使用通配符重新定義這個方法。

public class Util {

  public static int getCount(List<? extends Number> list)
  {
      int i = 0;
      for(Number n : list)
      {
          i++;
      }
      return i;

  }
}

  List<Integer> list = new ArrayList<Integer>(){
        {
            add(1);
            add(2);
            add(3);
        }
    };

    assertThat(Util.getCount(list), is(3));  // pass
既然能限定到一個類及其子類上,當然也能限定到一個類及其父類上。語法如下:

<? supper A>

對泛型使用的總結

  • 類型參數不能是原始類型(int, char,double),只能傳入這些類型的封轉類(Integer,Char,Double)。

  • 不能直接創建類型參數的實例。

public static <E> void append(List<E> list) {    E elem = new E();  // compile-time error    list.add(elem); 
}

但有通過反射可以實現。

public static <E> void append(List<E> list, Class<E> cls) throws Exception {    E elem = cls.newInstance();   // OK    list.add(elem);}
你可以這樣調用它:

List<String> ls = new ArrayList<>();
append(ls, String.class);
  • 靜態字段的類型不能爲類型參數。
public class Box<T> {    private static T object; // compile-time error

}
  • 不能創建類型參數變量的數組。
List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error
  • 不能重載一個方法,該方法的形參都來自於同一個類型參數對象。
 
public class Example {

  public void print(List<Integer> integers) {}

    public void print(List<Double> doubles) {}
}

參考文檔:http://docs.oracle.com/javase/tutorial/java/generics/index.html


發佈了42 篇原創文章 · 獲贊 13 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章