如何解决为什么在 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());
}
}
我是否遗漏了编译时与运行时的任何内容?为什么选择这样的设计?这背后的直觉是什么?
解决方法
这被 Java Lanaguage Spec 称为未经检查的缩小引用转换。允许这些强制转换对于构建一些通用代码很重要。例如,它们广泛用于实现 ArrayList
,其中元素存储在 Object[]
中,因此当项目添加到底层数组和从底层数组检索时,静态类型检查会丢失。但是,由于编译器无法静态地或在运行时执行检查,使用这些转换会导致未检查警告:
如果未选中缩小引用转换,则 Java 虚拟机将无法完全验证其类型正确性,可能会导致堆污染。为了向程序员标记这一点,未经检查的缩小引用转换会导致编译时未经检查的警告,除非被@SuppressWarnings 抑制
这些转换仅存在于源代码中;它们不会编译到字节码中,对运行时没有影响。他们只需要强制程序员声明他们知道(比编译器更好)转换是安全的。
,“JVM 对 T
是 String
还是其他任何东西一无所知”是正确的,因此在运行时不会发生任何事情。 >
通常,(引用类型)转换的运行时评估将涉及检查您正在转换的对象实际上是否属于您正在转换的类型。例如:
Object o = new Object();
String s = (String)o;
运行时对o
进行检查,会发现o
实际上是Object
类型而不是String
类型,因此, ClassCastException
将被抛出。
另一方面,如果您要强制转换为类型参数 T
,则运行时不知道 T
是什么,因此不进行任何检查,因此这是“未经检查的强制转换” ",正如警告所说。
因此,如果 val
实际上不是 T
类型,则不会抛出异常:
Cup<String> c = new Cup<>();
c.get();
即使我在上面的代码中调用了get
,并且执行了带有转换的行,但不会有异常,因为没有运行时检查。运行时认为 get
返回 Object
。 仅当运行时知道要强制转换的类型、是否发生强制转换并抛出异常时:
Cup<String> c = new Cup<>();
String s = c.get(); // this throws an exception
编译器像这样在第二行插入强制转换String s = (String)c.get();
如您所见,运行时不知道 T
在强制转换所在的行中是什么并不重要,因为您不需要需要无论如何投在那里。考虑代码的类型擦除版本:
public class Cup {
public Object get() {
Integer val = 1;
Object result = val;
return result;
}
public static void main(String[] args) {
Cup cup = new Cup();
System.out.println(cup.get());
}
}
您会注意到这是非常好的可以编译的代码!
(T)val
在这里主要是为了让编译器高兴,让编译器相信 val
确实是 T
类型。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。