[英]Why is usage of instanceof is not allowed, but Class.isInstance() is allowed in Generics?
我正在从 ThinkingInJava 中阅读泛型并找到了这个代码片段
public class Erased<T> {
private final int SIZE = 100;
public void f(Object arg) {
if(arg instanceof T) {} // Error
T var = new T(); // Error
T[] array = new T[SIZE]; // Error
T[] array = (T)new Object[SIZE]; // Unchecked warning
}
}
我理解擦除的概念,我知道在运行时,T 没有类型,它实际上被认为是一个对象(或任何上限)
但是,为什么这段代码有效
public class ClassTypeCapture<T> {
Class<T> kind;
public ClassTypeCapture(Class<T> kind) {
this.kind = kind;
}
public boolean f(Object arg) {
return kind.isInstance(arg);
}
}
我们不应该在这里也应用相同的论点,因为擦除我们不知道运行时 T 的类型,所以我们不能写这样的东西吗? 还是我在这里遗漏了什么?
在您的示例中, T
确实被删除了。 但是当你传递kind
,它是给定类型的类对象时,它可以完美地用于上述检查。
看看当你使用这个类时会发生什么:
ClassTypeCapture<String> capture = new ClassTypeCapture<>(String.class);
在这里, String
的类对象被传递给给定的构造函数,它从中创建一个新对象。
在类擦除期间, T
是String
的信息丢失了,但您仍然有
ClassTypeCapture capture = new ClassTypeCapture(String.class);
所以这个String.class
被保留并且被对象知道。
不同之处在于您确实在第二个代码段中引用了 java.lang.Class 的实例; 你一开始没有那个。
让我们看看第一个片段:只有一个Erased
实例作为一个类。 与看起来有点像泛型的 C 模板不同,C 模板为您放入泛型中的每个新类型生成一个新的类,在 Java 中只有一个 Erased 类。 因此,我们对 T 的全部了解就是您所看到的:它是一个类型变量。 它的名字是T。它的下限是'java.lang.Object'。 这就是它结束的地方。 没有隐藏在其中的Class<T>
类型的隐藏字段。
现在让我们看看第二个:
当然,同样的规则起初似乎适用,但在运行kind.isInstance
调用的上下文中,堆栈中有一个变量: kind
。 这可以是任何东西 - 通过一些花哨的转换和忽略警告,您可以创建一个new ClassTypeCapture<String>()
实例,传入Integer.class
。 这将编译甚至运行,然后可能会导致各种异常。
编译器只是通过执行一些编译时 lint 样式检查,真的会尝试告诉您,如果您尝试编写这样的代码,您不应该这样做,但这就是这里发生的全部。 至于JVM而言, String
在new ClassTypeCapture<String>(Integer.class)
和Integer
并不在所有相关除了一个编译时检查,说:仿制药不配套在这里,所以我会产生一个错误。 这是一个打破它的例子:
ClassTypeCapture /* raw */ a = new ClassTypeCapture<Integer>(String.class);
ClassTypeCapture<Integer> b = a;
这运行,并编译。 而 b's(与 a's 相同 - 相同引用)'kind' 字段引用String.class
。 这个对象的f()
方法的行为很奇怪。
我们不知道运行时 T 的类型
你错过了泛型的重点:泛型允许编译器“健全检查”类型,以确保它们是一致的。
很容易将ClassTypeCapture<T>
读作“类型T
的ClassTypeCapture
”,但它不是:它是一个ClassTypeCapture
,提示编译器检查所有涉及该引用的方法调用/字段访问/返回值是否一致类型为T
。
为了使这更具体(让我们用List
来做,这更容易):
List<String> list = new ArrayList<>();
list.add("hello");
String e = list.get(0);
<String>
是编译器执行此操作的指令:
List list = new ArrayList();
list.add("hello"); // Make sure this argument is a `String`
String e = (String) list.get(0); // Insert a cast here to ensure we get a String out.
在运行时,“ T
ness”不再为人所知,但ClassTypeCapture
(或Object
,或String
,或其他)仍然是。 您可以询问Object
是否是Object
、 String
、 ClassTypeCapture
或其他任何东西的实例。
你不能在编译时问它是否是ClassTypeCapture<String>
,因为<String>
只是一个编译器提示,而不是类型的一部分。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.