我正在从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的类型,所以我们不能写这样的东西吗?还是我在这里想念东西?
答案 0 :(得分:5)
在您的示例中,T
确实被删除了。但是,当您传递kind
(它是给定类型的类对象)时,它可以完美地用于所述检查。
看看使用此类时会发生什么:
ClassTypeCapture<String> capture = new ClassTypeCapture<>(String.class);
这里,String
的类对象被传递给给定的构造函数,该构造函数从中创建一个新对象。
在课堂删除期间,T
是String
的信息丢失了,但是您仍然有
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
(或Object
或String
或其他) 。您可以询问Object
是Object
,String
,ClassTypeCapture
还是其他实例。
您只是不能在编译时问它是否为ClassTypeCapture<String>
,因为该<String>
只是编译器提示,而不是类型的一部分。