1. 程式人生 > >Java SE7新特性之泛型例項建立時的型別推斷

Java SE7新特性之泛型例項建立時的型別推斷

只要編譯器從上下文中能夠推斷出型別引數,你就可以使用一個空的型別引數集合 (<>)代替呼叫一個泛型類的構造器所需要的型別引數。 這對尖括號通常叫做 diamond.

舉個例子, 考慮下面的變數宣告:

Map<String, List<String>> myMap = new HashMap<String, List<String>>();

在Java SE 7中, 你可以使用一個空的型別引數集合 (<>)代替構造器的引數化型別:

Map<String, List<String>> myMap = new HashMap<>();

注意:想要在泛型類初始化期間利用自動型別推斷,你必須要指定 diamond。下面的例子中,由於 HashMap() 構造器引用的是 HashMap 原始型別而不是 Map<String, List<String>> 型別,編譯器會產生一個未檢查的轉換警告:

Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning

Java SE 7對於例項建立的型別推斷的支援是有限的; 從上下文來看,只有構造器的引數化型別是明顯的才能使用型別推斷。 例如, 下面的例子編譯不通過:

List<String> list = new ArrayList<>();
list.add("A");

  // The following statement should fail since addAll expects
  // Collection<? extends String>

list.addAll(new ArrayList<>());

注意: diamond通常在方法呼叫中起作用;然而, 在變數宣告時建議首要使用diamond。

相比之下, 下面的例子可以編譯通過:

// The following statements compile:

List<? extends String> list2 = new ArrayList<>();
list.addAll(list2);

注意: 在泛型類和非泛型類中,構造器都可以是泛型的 (換句話說, 宣告它們自己的形式引數):

class MyClass<X> {
  <T> MyClass(T t) {
    // ...
  }
}

考慮以下 MyClass類的初始化,在Java SE 7以及之前的版本中都有效:

new MyClass<Integer>("")

這個語句建立一個引數化型別 MyClass<Integer>的一個例項; 它顯式的為泛型類 MyClass<X>指定 Integer 型別作為形式引數X 。 注意, 這個泛型類的構造器包含一個 形式引數。編譯器推斷這個泛型類的構造器的形式引數T的型別為 String  (因為這個構造器的實際引數是一個 String 物件)。

在Java SE 7之前,和泛型方法一樣,編譯器能夠推斷泛型構造器的實際引數。然而在 Java SE 7中,如果你使用diamond (<>),編譯器能夠推斷被例項化的泛型類的實際引數 。考慮下面的例子,在Java SE 7以及之後的版本中都有效:

MyClass<Integer> myObject = new MyClass<>("");

在這個例子中,編譯器推斷泛型類 MyClass<X> 的形式引數 X的型別為 Integer 。 並且推斷這個泛型類的構造器的形式引數T的型別為 String .