为什么在Java的类型转换过程中允许在右边使用(删除的)泛型类型?
这是不允许的,我认为这是由于类型删除。(T
被擦除,无法在运行时访问以读取其类,如T.class
)
class Cup<T> {
private T t;
public T[] getArray(int size) {
Class<T> cls = T.class;
return (T[]) Array.newInstance(cls, size);
}
}
但这是怎么编译的呢?我认为(T)val
的类型转换将在运行时发生,因此JVM将不知道T
是String
还是其他任何东西。所以javac应该完全阻止程序被编译。会出错
public class Cup<T> {
public T get() {
Integer val = 1;
T result = (T)val;
return result;
}
public static void main(String[] args) {
Cup<String> cup = new Cup<>();
System.out.println(cup.get());
}
}
我错过了编译时和运行时的任何东西吗?为什么选择这种设计?这背后的直觉是什么
# 1 楼答案
这被Java Lanaguage Spec称为未经检查的缩小参考转换。允许这些强制转换对于构建一些通用代码很重要。例如,它们广泛用于实现
ArrayList
,其中元素存储在Object[]
中,因此在向基础数组添加项并从中检索项时,静态类型检查会丢失。但是,由于编译器既不能静态执行检查,也不能在运行时执行检查,因此使用这些转换会导致未检查警告:这些转换只存在于源代码中;它们不会编译成字节码,对运行时没有影响。他们只需要强迫程序员声明(比编译器更了解)转换是安全的
# 2 楼答案
“JVM对
T
是String
还是其他任何东西都一无所知”,这是正确的,因此,在运行时不会发生任何事情通常,对(引用类型)强制转换的运行时评估将涉及检查您正在强制转换的对象是否实际属于您正在强制转换的类型。例如:
在运行时,对
o
执行检查,会发现o
实际上是Object
类型,而不是String
,因此会抛出ClassCastException
另一方面,如果要强制转换为类型参数
T
,则运行时不知道T
是什么,因此不进行任何检查,因此这是一个“未经检查的强制转换”,正如警告所说因此,如果
val
实际上不是T
类型,则不会引发异常:即使我在上面的代码中调用了
get
,并且执行了带强制转换的行,也不会有异常,因为没有运行时检查。运行时认为get
返回一个Object
。只有当运行时知道要强制转换的类型、是否发生强制转换以及引发异常时,才是:编译器在第二行插入强制转换,如
String s = (String)c.get();
正如您所看到的,运行时不知道强制转换所在行的
T
是什么并不重要,因为您无论如何都不需要强制转换。考虑代码的类型擦除:您会注意到,这是一个非常好的代码,可以编译
(T)val
这里主要是为了让编译器高兴,让编译器相信val
确实是类型T