Java 泛型是 Java 5 引入的一個(gè)重要特性,相信大多數(shù) Java 開(kāi)發(fā)者都對(duì)此不陌生,但是泛型背后的實(shí)現(xiàn)原理和類(lèi)型擦除還是許多人依然不是很清楚沧卢。本文將介紹 Java 泛型的原理和使用讥脐,重點(diǎn)闡述容易產(chǎn)生困惑的通配符、類(lèi)型擦除等問(wèn)題幔戏。
1. Java 泛型
1.1 Java 泛型是什么玛追?
Java 泛型,提供了參數(shù)化類(lèi)型,并且提供了編譯時(shí)強(qiáng)類(lèi)型檢查痊剖。泛型可以讓我們很簡(jiǎn)單地支持不同類(lèi)型韩玩,在 Java 集合框架中泛型廣泛用以對(duì)類(lèi)型的抽象。
1.2 Java 泛型的好處
提供編譯時(shí)的強(qiáng)類(lèi)型檢查陆馁≌彝牵可以在編譯時(shí)發(fā)現(xiàn)類(lèi)型安全問(wèn)題,不用等到運(yùn)行時(shí)叮贩。
避免類(lèi)型轉(zhuǎn)換击狮。
看下面一段代碼:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // type cast to String
如果使用泛型的話,不需要類(lèi)型轉(zhuǎn)換:
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); // no cast
- 可以實(shí)現(xiàn)通用的算法益老。通用算法可以處理不同類(lèi)型的集合彪蓬,可以進(jìn)行自定義,并且類(lèi)型安全且易于閱讀杨箭。
2. 泛型類(lèi)型與泛型方法
2.1 泛型類(lèi)型
泛型類(lèi)型是指泛型類(lèi)或泛型接口寞焙。為了理解泛型類(lèi)型的概念,看下面這個(gè)例子互婿。
先定義一個(gè)簡(jiǎn)單的 Box 類(lèi):
public class Box {
private String object;
public void set(String object) { this.object = object; }
public String get() { return object; }
}
上面代碼中的 Box 只能存放 String 類(lèi)型的元素捣郊,如果想存放 Integer 等其他類(lèi)型的元素,則必須重寫(xiě)另外一個(gè) Box慈参,代碼不能復(fù)用呛牲。下面再看使用泛型后的 Box:
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
現(xiàn)在 Box 可以存放除基本類(lèi)型外的任何類(lèi)型了。使用 T 類(lèi)型代替 String 類(lèi)型驮配,按照慣例娘扩,類(lèi)型參數(shù)名是一個(gè)大寫(xiě)字母,常見(jiàn)的類(lèi)型參數(shù)名如下:
E - Element(在 Java 集合框架中廣泛運(yùn)用)
K - Key
N - Number
T - Type
V - Value
2.2 泛型類(lèi)型的原始類(lèi)型(Raw Types)
原始類(lèi)型(Raw Types)是沒(méi)有指定參數(shù)類(lèi)型的泛型類(lèi)或泛型接口壮锻。例如琐旁,對(duì)于上面提到的Box<T>
泛型類(lèi):
Box rawBox = new Box();
Box
就是Box<T>
的原始類(lèi)型,原始類(lèi)型一般出現(xiàn)在舊版代碼中猜绣,因?yàn)榇罅康?API 在 Java 5 之前不是通用的灰殴。原始類(lèi)型和泛型類(lèi)型也可以轉(zhuǎn)換:
// generic type to raw type
Box<String> stringBox = new Box<>();
Box rawBox = stringBox; // OK
rawBox.set(8); // warning: unchecked invocation to set(T)
// raw type to generic type
Box rawBox = new Box(); // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox; // warning: unchecked conversion
2.3 泛型方法
泛型方法是指有它們自己參數(shù)化類(lèi)型的方法。類(lèi)型參數(shù)在一對(duì)尖括號(hào)之間掰邢,并且在方法返回類(lèi)型之前牺陶。
下面 Util 類(lèi)有一個(gè)泛型方法:
public class Util {
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
通常調(diào)用泛型方法的方式如下:
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);
// 如果在 Java 7 以上版本,利用類(lèi)型推斷可以簡(jiǎn)寫(xiě)為
boolean same = Util.compare(p1, p2);
3. 有界類(lèi)型參數(shù) (Bounded Type Parameters)
很多時(shí)候我們都想限制參數(shù)類(lèi)型的邊界辣之,例如在對(duì)比兩個(gè)對(duì)象的方法中掰伸,想確保方法參數(shù)都是 Comparable 的。聲明有界類(lèi)型參數(shù)(Bounded Type Parameters
)怀估,格式為T extends Class & Interface1 & ... & InterfaceN
狮鸭。
public static <T extends Comparable<T>> int compare(T t1, T t2){
return t1.compareTo(t2);
}
這樣當(dāng)我們傳遞的參數(shù)沒(méi)有實(shí)現(xiàn) Comparable 接口,會(huì)有編譯時(shí)錯(cuò)誤。有界類(lèi)型參數(shù)同樣可以用于泛型類(lèi)和泛型接口中怕篷,而且支持多個(gè)邊界历筝,例如 <T extends A & B & C>
酗昼,只允許最多一個(gè) Class 邊界廊谓,而且如果有一個(gè) Class 邊界竿裂,Class 邊界必須在最前面险污。
4. 通配符
Java 泛型中問(wèn)號(hào)?
是通配符,表示未知類(lèi)型虫碉。通配符可以用于參數(shù)呛哟、屬性叠荠、局部變量或返回值的類(lèi)型,但是不能用于泛型方法調(diào)用或創(chuàng)建泛型類(lèi)實(shí)例的類(lèi)型參數(shù)扫责。
4.1 無(wú)界通配符 (Unbounded Wildcards)
單獨(dú)使用?
表示無(wú)界通配符榛鼎,例如List<?>
,表示未知類(lèi)型的 list鳖孤。下面兩個(gè)場(chǎng)景適合使用無(wú)界通配符:
如果想寫(xiě)一個(gè)方法者娱,只用到 Object 類(lèi)中的功能,即用
List<?>
代替List<Object>
苏揣。當(dāng)使用到的泛型類(lèi)型的方法不依賴(lài)參數(shù)類(lèi)型時(shí)黄鳍,例如只用到 List.size 或 List.clear 方法。事實(shí)上平匈,
Class<?>
非常常見(jiàn)也是因?yàn)?code>Class<T>中的大多數(shù)方法都不依賴(lài) T框沟。
4.2 上限通配符 (Upper Bounded Wildcards)
上限通配符可以放寬對(duì)變量的限制。語(yǔ)法為? extends SuperType
增炭,SuperType 可以是類(lèi)或接口忍燥。例如,如果想寫(xiě)一個(gè)對(duì) List<Integer>
, List<Double>
, List<Number>
都適用的方法隙姿,可以用 List<? extends Number>
:
public static double sumOfList(List<? extends Number> list) {
double s = 0.0;
for (Number n : list)
s += n.doubleValue();
return s;
}
4.3 下限通配符 (Lower Bounded Wildcards)
下限通配符可以限制為特定類(lèi)型或該類(lèi)型的父類(lèi)型梅垄。語(yǔ)法為? super SubType
。例如孟辑,想寫(xiě)一個(gè)方法添加 Integer 對(duì)象到 list 中哎甲,可以是 List<Integer>
, List<Number>
和List<Object>
,可以用List<? super Integer>
:
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
4.4 泛型的繼承與子類(lèi)型
泛型有個(gè)常見(jiàn)的誤解:Integer 是 Number 的子類(lèi)型饲嗽,所以 Box<Integer>
也是 Box<Number>
的子類(lèi)型炭玫。但是其實(shí)Box<Integer>
和Box<Number>
并沒(méi)有直接關(guān)系。
public void boxTest(Box<Number> n) { /* ... */ }
// 如果傳 Box<Integer> 會(huì)出現(xiàn)編譯錯(cuò)誤
泛型類(lèi)的繼承貌虾,可以看 Collection 的關(guān)系吞加,ArrayList<String>
是List<String>
的子類(lèi)型:
上面提到Box<Integer>
和Box<Number>
都是 Object 子類(lèi),但是它們還有個(gè)共有的父類(lèi)型Box<?>
,同理List<Number>
和List<Integer>
的父類(lèi)型為List<?>
衔憨。
至于上限通配符和下限通配符間的關(guān)系叶圃,見(jiàn)下圖:
4.5 通配符捕獲 (Wildcard Capture)
有些時(shí)候,編譯器會(huì)推測(cè)通配符的類(lèi)型践图,例如List<?>
類(lèi)型掺冠,在某些代碼中編譯器從代碼推斷出具體的類(lèi)型,這種場(chǎng)景就是通配符捕獲码党。大多數(shù)情況下德崭,我們都不需要關(guān)心通配符捕獲,除非看到錯(cuò)誤信息中包含“CAP#”揖盘。
下面代碼會(huì)產(chǎn)生捕獲錯(cuò)誤:
public class WildcardError {
void foo(List<?> i) {
i.set(0, i.get(0)); // 錯(cuò)誤: 不兼容的類(lèi)型: Object無(wú)法轉(zhuǎn)換為CAP#1
}
}
上面代碼中的錯(cuò)誤一開(kāi)始可能覺(jué)得莫名其妙眉厨,先看看錯(cuò)誤信息:i.set(int, capture<?>) 需要的參數(shù)類(lèi)型為int,CAP#1
,而實(shí)際傳入的為int,Object
兽狭,編譯器將 i.get(0) 返回的類(lèi)型推斷為 Object憾股。當(dāng)調(diào)用List.set(int, E)
時(shí),編譯器無(wú)法確認(rèn)傳入的類(lèi)型與 List 的元素類(lèi)型一致箕慧,雖然我們?nèi)藶橹肋@處調(diào)用的類(lèi)型是一致的服球。
我們可以額外加一個(gè)泛型方法來(lái)避免編譯錯(cuò)誤:
public class WildcardFixed {
void foo(List<?> i) {
fooHelper(i);
}
// Helper method created so that the wildcard can be captured
// through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0)); // 傳入?yún)?shù)也為 T,編譯器推斷為 CAP#1
}
}
下面再看一個(gè)更復(fù)雜的例子:
public class WildcardErrorBad {
void swapFirst(List<? extends Number> l1, List<? extends Number> l2) {
Number temp = l1.get(0);
l1.set(0, l2.get(0)); // 錯(cuò)誤: 不兼容的類(lèi)型: Number無(wú)法轉(zhuǎn)換為CAP#1
l2.set(0, temp); // 錯(cuò)誤: 不兼容的類(lèi)型: Number無(wú)法轉(zhuǎn)換為CAP#1
}
}
這個(gè)例子試圖執(zhí)行一個(gè)不安全的操作销钝,看下面調(diào)用的場(chǎng)景:
List<Integer> li = Arrays.asList(1, 2, 3);
List<Double> ld = Arrays.asList(10.10, 20.20, 30.30);
swapFirst(li, ld);
雖然List<Integer>
和List<Double>
都符合List<? extends Number>
類(lèi)型有咨,但是List<Integer>
列表中存放 Double 類(lèi)型的元素顯然不正確,所以也無(wú)法添加其他泛型方法來(lái)解決這個(gè)問(wèn)題蒸健。
4.6 PECS 原則
在學(xué)習(xí)泛型的過(guò)程座享,一個(gè)容易困惑的問(wèn)題是如何什么時(shí)候用上限通配符或下限通配符。下面先分析兩者的具體使用區(qū)別似忧。
對(duì)于List<? extends Number>
類(lèi)型渣叛,可以執(zhí)行哪些操作呢?
List<? extends Number> list = new ArrayList<Integer>();
Number first = list.get(0); // OK
list.add(null); // OK
Number number = 1;
list.add(number); // 錯(cuò)誤: 不兼容的類(lèi)型: Number無(wú)法轉(zhuǎn)換為CAP#1
list.clear(); // OK
list.remove(0); // OK
List<? extends Number>
類(lèi)型可以添加 null 值盯捌,也可以通過(guò)泛型方法寫(xiě)入從本身 list 讀取的值淳衙,但是無(wú)法添加新的元素。無(wú)法添加新元素的原因饺著,是對(duì)于List<? extends Number>
類(lèi)型來(lái)說(shuō)箫攀,可能是 List<Number>
、 List<Integer>
或List<Double>
等類(lèi)型幼衰,無(wú)法確定新元素的類(lèi)型與集合里的類(lèi)型一致靴跛,所以編譯器會(huì)提示報(bào)錯(cuò)。所以可以將List<? extends Number>
類(lèi)型的列表看作非嚴(yán)格意義上的只讀列表渡嚣。
而對(duì)于List<? super Number>
類(lèi)型呢:
List<? super Number> list = new ArrayList<Object>();
Number first = list.get(0); // 錯(cuò)誤: 不兼容的類(lèi)型: CAP#1無(wú)法轉(zhuǎn)換為Number
list.add(null); // OK
Number number = 1;
list.add(number); // OK
list.clear(); // OK
list.remove(0); // OK
List<? super Number>
類(lèi)型可以添加 null 值梢睛、添加新的元素肥印,也可以刪除元素,但是無(wú)法讀取列表中的值绝葡。無(wú)法讀取列表的原因深碱,是對(duì)于List<? super Number>
類(lèi)型來(lái)說(shuō),可能是List<Number>
也可能是List<Object>
類(lèi)型藏畅,讀取列表元素時(shí)不能確定元素類(lèi)型敷硅。所以可以將List<? super Number>
類(lèi)型的列表看作只寫(xiě)列表。
上面兩個(gè)例子中墓赴,只讀類(lèi)型相當(dāng)于生產(chǎn)者(Producer)竞膳,生產(chǎn) T,就使用? extends T
诫硕,只寫(xiě)類(lèi)型相當(dāng)于消費(fèi)者(Consumer),消費(fèi) T刊侯,就使用? super T
章办。也就是“Producer Extends, Consumer Super”,簡(jiǎn)稱(chēng) PECS 原則滨彻。
Collections.copy 方法就用到了這個(gè)原則藕届,copy(List<? super T> dest, List<? extends T> src)
,src 列表是只讀的亭饵,dest 列表是只寫(xiě)的休偶。
通配符的使用建議如下:
只讀類(lèi)型使用上限通配符
? extends T
只寫(xiě)類(lèi)型使用下限通配符
? super T
如果只讀類(lèi)型只用到 Object 的方法,即
List<? extends Object>
辜羊,可以用List<?>
無(wú)界通配符對(duì)于同時(shí)需要讀取和寫(xiě)入的類(lèi)型踏兜,不要使用通配符
上面四條建議都不適用于方法返回值類(lèi)型。應(yīng)該避免在返回值中使用通配符八秃,因?yàn)檫@樣會(huì)強(qiáng)制要求調(diào)用者調(diào)用時(shí)處理通配符碱妆。
5. 類(lèi)型擦除
類(lèi)型擦除是 Java 泛型中最容易產(chǎn)生困惑的地方,舉個(gè)很簡(jiǎn)單的例子昔驱,許多人誤以為List<String>
與List<Integer>
的 Class 類(lèi)型不一致:
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
System.out.println(strList.getClass().getName()); // java.util.ArrayList
System.out.println(intList.getClass().getName()); // java.util.ArrayList
System.out.println(strList.getClass() == intList.getClass()); // true
在編譯時(shí)List<String>
和List<Integer>
的類(lèi)型是不一樣的疹尾,但是在運(yùn)行時(shí)兩者的類(lèi)型又是一樣的,背后的原因就是類(lèi)型擦除骤肛。
Java 泛型添加是為了提供編譯時(shí)的類(lèi)型檢查和支持泛型編程纳本,并沒(méi)有運(yùn)行時(shí)的支持。所以 Java 編譯器會(huì)用類(lèi)型擦除來(lái)刪除所有泛型類(lèi)型檢查代碼腋颠,并在必要時(shí)插入強(qiáng)制類(lèi)型轉(zhuǎn)換繁成。類(lèi)型擦除確保不為參數(shù)化類(lèi)型創(chuàng)建新類(lèi),所以ArrayList<E>
的 Class 類(lèi)型還是java.util.ArrayList
秕豫,相應(yīng)的朴艰,泛型也不會(huì)增加運(yùn)行時(shí)開(kāi)銷(xiāo)观蓄。Java 編譯器在應(yīng)用泛型類(lèi)型擦除時(shí)有以下行為:
將泛型中所有參數(shù)化類(lèi)型替換為泛型邊界,如果參數(shù)化類(lèi)型是無(wú)界的祠墅,則替換為 Object 類(lèi)型侮穿。字節(jié)碼中沒(méi)有任何泛型的相關(guān)信息。
為了類(lèi)型安全毁嗦,在必要時(shí)插入類(lèi)型轉(zhuǎn)換代碼亲茅。
生成橋接方法來(lái)保持泛型類(lèi)型的多態(tài)性。
5.1 參數(shù)化類(lèi)型替換
對(duì)于無(wú)解參數(shù)化類(lèi)型狗准,類(lèi)型擦除時(shí)會(huì)替換為 Object克锣。
下面看單鏈表中節(jié)點(diǎn)類(lèi):
public class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
經(jīng)過(guò)類(lèi)型擦除后:
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() { return data; }
// ...
}
對(duì)于有界參數(shù)化類(lèi)型,類(lèi)型擦除時(shí)會(huì)替換為第一個(gè)邊界腔长。
如果節(jié)點(diǎn)類(lèi)使用有界參數(shù)化類(lèi)型:
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
經(jīng)過(guò)類(lèi)型擦除后:
public class Node {
private Comparable data;
private Node next;
public Node(Comparable data, Node next) {
this.data = data;
this.next = next;
}
public Comparable getData() { return data; }
// ...
}
5.2 類(lèi)型轉(zhuǎn)換
經(jīng)過(guò)參數(shù)化類(lèi)型替換后袭祟,在使用泛型相關(guān)內(nèi)容時(shí),通常需要添加類(lèi)型轉(zhuǎn)換代碼捞附,看下面代碼:
Node<String> node = new Node<>("Hello", null);
String data = node.getData(); // 實(shí)際上 node.getData() 返回的是 Object 類(lèi)型
所以編譯器還會(huì)插入類(lèi)型轉(zhuǎn)換代碼巾乳,編譯后如下:
Node node = new Node("Hello", null);
String data = (String) node.getData();
5.3 橋接方法
當(dāng)編譯一個(gè)類(lèi)繼承泛型類(lèi)或泛型接口,在類(lèi)型擦除的過(guò)程中編譯器會(huì)生成一個(gè)合成方法鸟召,也稱(chēng)為橋接方法胆绊。
看下面代碼:
interface Comparable <A> {
public int compareTo( A that);
}
final class NumericValue implements Comparable <NumericValue> {
priva te byte value;
public NumericValue (byte value) { this.value = value; }
public byte getValue() { return value; }
public int compareTo( NumericValue t hat) { return this.value - that.value; }
}
經(jīng)過(guò)參數(shù)化類(lèi)型替換后,Comparable 接口的 compareTo 方法的參數(shù)類(lèi)型為 Object欧募,而 NumericValue 也需要實(shí)現(xiàn)compareTo(Object)
方法压状,經(jīng)過(guò)類(lèi)型擦除后:
interface Comparable {
public int compareTo( Object that);
}
final class NumericValue implements Comparable {
priva te byte value;
public NumericValue (byte value) { this.value = value; }
public byte getValue() { return value; }
public int compareTo( NumericValue t hat) { return this.value - that.value; }
// 新合成的橋接方法
public int compareTo(Object that) { return this.compareTo((NumericValue)that); }
}
類(lèi)型擦除后NumericValue.compareTo(NumericValue)
方法不再是接口的實(shí)現(xiàn)方法,這是類(lèi)型擦除的一個(gè)副作用:兩個(gè)方法(在接口和實(shí)現(xiàn)類(lèi)中)在類(lèi)型擦除之前具有相同的簽名跟继,而在類(lèi)型擦除之后具有不同的簽名种冬。
為了讓 NumericValue 依然正確地實(shí)現(xiàn) Comparable 接口,編譯器添加了一個(gè)橋接方法还栓,和接口的簽名相同碌廓,橋接方法委托給實(shí)現(xiàn)類(lèi)中的原始方法。
雖然存在橋接方法剩盒,但是一般情況下谷婆,編譯器不允許我們調(diào)用橋接方法:
NumericValue value = new NumericValue((byte) 0);
value.compareTo(value); // OK
value.compareTo("abc"); // error
但是,還有兩種方式可以調(diào)用橋接方法:使用原始類(lèi)型(Raw Types)或反射辽聊。但是橋接方法中有類(lèi)型轉(zhuǎn)換纪挎,所以傳其他類(lèi)型會(huì)有運(yùn)行時(shí)報(bào)錯(cuò)。下面是使用原始類(lèi)型的例子:
Comparable comparable = new NumericValue((byte) 0);
comparable.compareTo(comparable); // OK
comparable.compareTo("abc"); // OK at compile time, throws ClassCastException at run time
6. 泛型的限制
6.1 不能用基本類(lèi)型實(shí)例化泛型
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
// ...
}
Pair<int, char> p = new Pair<>(8, 'a'); // compile-time error
Pair<Integer, Character> p = new Pair<>(8, 'a'); // ok跟匆,because of autoboxing
6.2 不能創(chuàng)建參數(shù)化類(lèi)型的實(shí)例
不能創(chuàng)建參數(shù)化類(lèi)型的實(shí)例异袄,但是可以用反射創(chuàng)建:
public static <E> void append(List<E> list, Class<E> cls) throws Exception {
E e1 = new E(); // error
E e2 = cls.newInstance(); // ok
list.add(e2);
}
6.3 不能將靜態(tài)屬性聲明為泛型類(lèi)型
類(lèi)的靜態(tài)屬性是類(lèi)級(jí)別的屬性,被該類(lèi)所有實(shí)例共享玛臂,所以不允許靜態(tài)屬性是參數(shù)化類(lèi)型:
public class MobileDevice<T> {
private static T os; // compile-time error, if has MobileDevice<Phone> and MobileDevice<Pc> instance, can not confirm the type of os.
}
6.4 不能對(duì)參數(shù)化類(lèi)型使用 Casts 或 instanceof
不能 Casts 為參數(shù)化類(lèi)型烤蜕,除非是無(wú)界通配符類(lèi)型:
List<Integer> li = new ArrayList<>();
List<?> list = li;
List<Number> ln = (List<Number>) li; // compile-time error
但是有些場(chǎng)景封孙,編譯器知道參數(shù)化類(lèi)型是合法的,也會(huì)運(yùn)行類(lèi)型轉(zhuǎn)換:
List<String> l1 = new ArrayList<>();
ArrayList<String> l2 = (ArrayList<String>)l1; // OK
因?yàn)轭?lèi)型擦除讽营,無(wú)法確定運(yùn)行時(shí)參數(shù)化類(lèi)型具體是什么類(lèi)型虎忌,所以無(wú)法使用 instanceof 校驗(yàn)類(lèi)型:
public static <E> void rtti(List<E> list) {
if (list instanceof ArrayList<Integer>) { // compile-time error
// ...
}
if (list instanceof ArrayList<?>) { // OK; instanceof requires a reifiable type
// ...
}
}
6.5 不能創(chuàng)建參數(shù)化類(lèi)型的數(shù)組
ArrayList<String>[] arrayOfList = new ArrayList<String>[3]; // compile-time error
6.6 不能創(chuàng)建、捕捉或拋出參數(shù)化類(lèi)型的對(duì)象
泛型類(lèi)不能直接或間接地繼承Throwable
類(lèi):
// Extends Throwable indirectly
class MathException<T> extends Exception { /* ... */ } // compile-time error
// Extends Throwable directly
class QueueFullException<T> extends Throwable { /* ... */ } // compile-time error
也無(wú)法捕捉參數(shù)化類(lèi)型的異常:
public static <T extends Exception, J> void execute(List<J> jobs) {
try {
for (J job : jobs)
// ...
} catch (T e) { // compile-time error
// ...
}
}
但是橱鹏,可以在 throws 語(yǔ)句中使用參數(shù)化類(lèi)型:
class Parser<T extends Exception> {
public void parse(File file) throws T { // OK
// ...
}
}
6.7 不能重載參數(shù)類(lèi)型為相同原始類(lèi)型的方法
不能有兩個(gè)重載方法膜蠢,當(dāng)他們的方法簽名在類(lèi)型擦除后是一樣的。
public class Example {
public void print(List<String> list) {}
public void print(List<Integer> list) {}
}
推薦閱讀: