将 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