Java擦除如何影响通用数组?

ace*_*azz 10 java arrays generics erasure

我正在研究这一时期的仿制药,今天我已经为我找到了这个谜.

让我们考虑以下虚拟类:

    public class Main{

    public static void main(String[] args) { 
        Container<Integer> c = new Container<Integer>(); 

        c.getArray();                      //No Exception
        //c.getArray().getClass();         //Exception
        //int a = c.getArray().length;     //Exception

    } 

}


class Container<T> { 

    T[] array; 

    @SuppressWarnings("unchecked") 
    Container() { 
        array = (T[])new Object[1]; 
    } 

    void put(T item) { 
        array[0] = item; 
    } 

    T get() { return array[0]; } 

    T[] getArray() { return array; }
}  
Run Code Online (Sandbox Code Playgroud)

由于擦除,在运行时,getArray()方法的T []返回类型变为Object [],这对我来说是完全合理的.

如果我们按原样访问该方法(c.getArray())没有抛出异常,但是如果我们尝试在返回的数组上调用某些方法,例如c.Array().getClass(),或者如果我们尝试访问一个字段,例如c.getArray().length,然后抛出以下异常:

线程"main"中的异常java.lang.ClassCastException:[Ljava.lang.Object; 无法转换为[Ljava.lang.Integer;

为什么抛出此异常?为什么不对简单的c.getArray()调用抛出它?如果我们只是调用getClass()或访问长度,它为什么要尝试强制转换为Integer []?对于[],getClass()和长度是否也不可用?

在此先感谢您的许多(我希望)和解释(我也希望这样)答案.

new*_*cct 1

当您执行不安全的未经检查的强制转换时,它可能会也可能不会在某个地方引起异常。不保证您会在某个地方遇到异常。

在这种情况下,是否出现异常取决于编译器是否在擦除的代码中插入了强制转换以将调用结果强制转换为Integer[]. 在本例中,似乎在第二个和第三个案例中插入了演员表,但在第一个案例中没有插入。

在这三种情况中的每一种中,都允许编译器插入强制转换(因为允许假设结果是否Integer[]插入强制转换(因为表达式的使用方式只需要Object[]在所有三种情况下)。是否插入强制转换由特定的编译器实现决定。

为什么这个编译器不在第一种情况下插入强制转换,而在第二种和第三种情况下插入强制转换?一个明显的解释是,在第一种情况下,结果显然未使用,因此很容易确定不需要强制转换。在第二种和第三种情况下,要确定强制转换是不必要的,需要查看表达式的使用方式,以了解它是否也适用于Object[]; 这是一个相当复杂的分析。编译器作者可能选择了一种简单的方法,仅在结果未使用时才跳过强制转换。

另一个编译器可能会在所有三种情况下插入强制转换。而另一个编译器可能在所有三种情况下都没有强制转换。你不能依赖它。