为什么在 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 不会知道 TString 还是其他任何东西。所以 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 对 TString 还是其他任何东西一无所知”是正确的,因此在运行时不会发生任何事情。 >

通常,(引用类型)转换的运行时评估将涉及检查您正在转换的对象实际上是否属于您正在转换的类型。例如:

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 类型。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...