将 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