將 instanceof 與泛型一起使用
使用泛型來定義 instanceof 中的型別
考慮使用形式引數 <T>
宣告的以下泛型類 Example
:
class Example<T> {
public boolean isTypeAString(String s) {
return s instanceof T; // Compilation error, cannot use T as class type here
}
}
這將始終產生編譯錯誤,因為只要編譯器將 Java 原始碼編譯為 Java 位元組碼,它就會應用稱為型別擦除的過程,該過程將所有通用程式碼轉換為非通用程式碼,從而無法在執行時區分 T 型別。與 instanceof
一起使用的型別必須是 可恢復的 ,這意味著有關該型別的所有資訊必須在執行時可用,而泛型型別通常不是這種情況。
下面的類代表了兩個不同類別的 Example
,Example<String>
和 Example<Number>
,在仿製品被型別擦除剝離後的樣子 :
class Example { // formal parameter is gone
public boolean isTypeAString(String s) {
return s instanceof Object; // Both <String> and <Number> are now Object
}
}
由於型別已經消失,JVM 無法知道哪種型別是 T
。
先前規則的例外情況
你始終可以使用無界萬用字元 (?)在 instanceof
中指定型別,如下所示:
public boolean isAList(Object obj) {
return obj instanceof List<?>;
}
這可用於評估例項 obj
是否為 List
:
System.out.println(isAList("foo")); // prints false
System.out.println(isAList(new ArrayList<String>()); // prints true
System.out.println(isAList(new ArrayList<Float>()); // prints true
實際上,無界萬用字元被認為是可再生的型別。
使用 instanceof 的通用例項
硬幣的另一面是使用 T
與 instanceof
的例項 t
是合法的,如下例所示:
class Example<T> {
public boolean isTypeAString(T t) {
return t instanceof String; // No compilation error this time
}
}
因為在型別擦除之後,類將如下所示:
class Example { // formal parameter is gone
public boolean isTypeAString(Object t) {
return t instanceof String; // No compilation error this time
}
}
因為,即使型別擦除仍然發生,現在 JVM 可以區分記憶體中的不同型別,即使它們使用相同的引用型別(Object
),如下面的程式碼片段所示:
Object obj1 = new String("foo"); // reference type Object, object type String
Object obj2 = new Integer(11); // reference type Object, object type Integer
System.out.println(obj1 instanceof String); // true
System.out.println(obj2 instanceof String); // false, it's an Integer, not a String