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

时间:2020-01-29 15:45:02

标签: java generics

我正在从ThinkingInJava阅读有关泛型的内容,并找到了此代码段

list

我了解擦除的概念,并且我知道在运行时,T没有类型,它实际上被视为对象(或任何上限)

但是,这段代码为什么起作用

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的类型,所以我们不能写这样的东西吗?还是我在这里想念东西?

3 个答案:

答案 0 :(得分:5)

在您的示例中,T确实被删除了。但是,当您传递kind(它是给定类型的类对象)时,它可以完美地用于所述检查。

看看使用此类时会发生什么:

ClassTypeCapture<String> capture = new ClassTypeCapture<>(String.class);

这里,String的类对象被传递给给定的构造函数,该构造函数从中创建一个新对象。

在课堂删除期间,TString的信息丢失了,但是您仍然有

ClassTypeCapture capture = new ClassTypeCapture(String.class);

因此String.class被保留并为对象所知。

答案 1 :(得分:3)

区别在于您在第二个片段中确实引用了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的(与a的相同-相同的引用)“种类”字段引用String.class。该对象的f()方法的行为非常奇怪。

答案 2 :(得分:3)

我们在运行时不知道T的类型

您遗漏了泛型的要点:泛型允许编译器“检查”类型,以确保它们是一致的。

很容易将ClassTypeCapture<T>读为“ ClassTypeCapture类型的T”,但这不是 :它是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(或ObjectString或其他) 。您可以询问ObjectObjectStringClassTypeCapture还是其他实例。

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