將 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 一起使用的型別必須是 可恢復的 ,這意味著有關該型別的所有資訊必須在執行時可用,而泛型型別通常不是這種情況。

下面的類代表了兩個不同類別的 ExampleExample<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 的通用例項

硬幣的另一面是使用 Tinstanceof 的例項 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