有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

为什么在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());
    }
}

我错过了编译时和运行时的任何东西吗?为什么选择这种设计?这背后的直觉是什么


共 (2) 个答案

  1. # 1 楼答案

    这被Java Lanaguage Spec称为未经检查的缩小参考转换。允许这些强制转换对于构建一些通用代码很重要。例如,它们广泛用于实现ArrayList,其中元素存储在Object[]中,因此在向基础数组添加项并从中检索项时,静态类型检查会丢失。但是,由于编译器既不能静态执行检查,也不能在运行时执行检查,因此使用这些转换会导致未检查警告

    If a narrowing reference conversion is unchecked, then the Java Virtual Machine will not be able to fully validate its type correctness, possibly leading to heap pollution. To flag this to the programmer, an unchecked narrowing reference conversion causes a compile-time unchecked warning, unless suppressed by @SuppressWarnings

    这些转换只存在于源代码中;它们不会编译成字节码,对运行时没有影响。他们只需要强迫程序员声明(比编译器更了解)转换是安全的

  2. # 2 楼答案

    “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