繁体   English   中英

为什么不允许使用 instanceof,但在泛型中允许 Class.isInstance()?

[英]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的类对象被传递给给定的构造函数,它从中创建一个新对象。

在类擦除期间, TString的信息丢失了,但您仍然有

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而言, Stringnew 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>读作“类型TClassTypeCapture ”,但它不是:它是一个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是否是ObjectStringClassTypeCapture或其他任何东西的实例。

你不能在编译时问它是否是ClassTypeCapture<String> ,因为<String>只是一个编译器提示,而不是类型的一部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM