題目來源:【Java每日一題】20170117
package Jan2017;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Ques0117 {
public static void main(String[] args) {
List list = Ques0117.asList(1, 2, 3.0);
}
public static List asList(T... t) {
List list = new ArrayList();
Collections.addAll(list, t);
return list;
}
}
list 的泛型參數類型是什麼?是 Number 嗎?調用 asList(1, 2, 3.0) 時,實參類型為 int, int, double。經過自動裝箱變為 Integer, Integer, Double。
Java 泛型類型推導機制會尋找所有實參的最特化公共超類型(Least Upper Bound, LUB)。
Integer 繼承 Number 實現 Comparable<Integer>, SerializableDouble 繼承 Number 實現 Comparable<Double>, SerializableNumber & Comparable<?> & Serializable(交集類型)因此推導出的 T 為 Number & Comparable<?> & Serializable,而非單純的 Number。
List list(原始類型)代碼中聲明 List list(無泛型參數),屬於原始類型,編譯器會發出「未檢查」警告,但合法。若改為 List<Number> list = ... 則編譯錯誤,因為 List<Number & Comparable<?> & Serializable> 不是 List<Number> 的子類型(泛型不變性)。
由於類型擦除,運行時 list 就是 ArrayList,實際存儲 Integer, Integer, Double 三個對象,獲取時無需強轉(原始類型視為 Object)。
<Number>asList(...) 或賦值給 List<Number> 觸發推導。lub(T1, T2) = 最小公共超類型,包含所有共同接口。Number & Comparable<?> & Serializable 只能作為類型參數或強轉目標,不能作為變量聲明類型(Java 語法限制)。List 繞過泛型檢查,失去類型安全,不推薦。// 明確指定類型參數 Listlist = Ques0117. asList(1, 2, 3.0); // 編譯通過,T=Number // 或使用顯式構造 List list = new ArrayList<>(); Collections.addAll(list, 1, 2, 3.0); // 自動裝箱為 Number 子類
理解泛型推導規則與 LUB,能避免「為什麼這裡推導出這個怪異類型」的困惑,編寫更精確的泛型代碼。