为什么ArrayStoreException是RuntimeException?

时间:2016-07-15 08:14:58

标签: java arrays exception exception-handling runtimeexception

假设我们有以下计划:

class Fruit {}

class Apple extends Fruit {} 

class Jonathan extends Apple {} 

class Orange extends Fruit {} 

public class Main { 
    public static void main(String[] args) { 
        Fruit[] fruit = new Apple[10];

        try { 
            fruit[0] = new Fruit(); // ArrayStoreException 
            fruit[0] = new Orange(); // ArrayStoreException 
        } catch(Exception e) { System.out.println(e); } 
    } 
}

基于Java documentation

  

抛出表示已尝试存储错误   对象类型到对象数组中。

我已阅读here

  

创建数组时,它会记住它要存储的数据类型。

如果数组记住它包含的数据类型,则意味着它知道它包含的数据类型。但是我发布的代码片段是正确编译的,所以在编译时,数组显然不知道包含什么类型。

我的问题是:

  1. 为什么ArrayStoreException仅在运行时抛出?

  2. 编译器缺少哪些信息才能意识到这种分配是不可能的?

  3. 是否存在此类代码正确的情况,因此不会抛出ArrayStoreException

4 个答案:

答案 0 :(得分:5)

  

如果数组记住它包含的数据类型,则意味着它知道它包含的数据类型。

在执行时,是......就像在执行时一样,对象的类型是已知的:

Object x = "foo";
// The compiler won't let you call x.length() here, because the variable
// x is of type Object, not String.

另一种方法是使非常多的数组赋值隐式抛出一个已检查的异常。这很糟糕 - 类似于NullPointerException检查。

  

编译器缺少哪些信息才能意识到这种分配是不可能的?

如你所见,阵列是协变的。当编译器看到Fruit[]的{​​{1}}分配时,它无法知道该数组的实际类型是什么。如果是AppleFruit[],那很好。如果它是Apple[]则不是。该信息仅在执行时出现 - 再次,就像编译器不知道表达式是否绝对不为空。

  

是否存在此类代码正确的情况,因此不会抛出ArrayStoreException?

如果你有一个具有最终类的编译时元素的数组,那么你就不能有任何较低的方差。例如:

Orange[]

由于public void foo(String[] array) { array[0] = "x"; } array或为空,可能会抛出异常,但它永远不会抛出null,因为ArrayStoreException是最终的。实施永远不可能是String

答案 1 :(得分:3)

由于ClassCastException的原因相同,它是运行时异常。在编译时并不总能告诉类型是否符合您的期望。

考虑这个例子:

void method1() {
    Fruit[] fruits = getFruits();
    fruits[0] = new Orange();
}

Fruit[] getFruits() {
    if (someCondition) {
        return new Apple[5];
    } else {
        return new Orange[5];
    }
}

当您致电someCondition时,编译器无法知道状态getFruits()将处于什么状态。因此运行时异常。

答案 2 :(得分:1)

  

创建数组时,它会记住它的数据类型   存储。

数组“只记住”它在运行时实际包含的类型。

首先声明数组,在本例中是一个Fruit数组。

然后创建数组,在本例中为Apple数组。

在运行时进行创建,但编译器仅用于验证是否仅为数组分配了声明类型的对象。在运行时期间可能会发生很多事情。

请考虑以下代码:

class Fruit {}

class Apple extends Fruit {} 

class Jonathan extends Apple {} 

class Orange extends Fruit {} 

public class Main { 
    public static void main(String[] args) { 
        Fruit[] fruit = new Apple[10];
        boolean alt = (Math.random() < 0.5);

        try { 
            fruit[0] = fruitFactory(alt); 
        } catch(Exception e) { System.out.println(e); } 
    } 

    private static Fruit fruitFactory(boolean apple) {
        if (apple) {
            return new Apple();
        } else {
            return new Orange();
        }
    } 
}

除了fruitFactory方法为fruit [0]赋值之外,代码与您的代码相同。编译器无法判断boolean alt是true还是false

  

编译器缺少哪些信息才能实现这一点   分配是不可能的?

如上所述 - 编译器无法判断分配是否可行。

  

是否存在此类代码正确的情况,因此没有   抛出ArrayStoreException?

是的,在上面代码的50%的情况下。您必须验证分配的对象是否与数组相同或捕获异常。

答案 3 :(得分:-2)

在你的情况下,苹果和橙子被隐含地浇进水果中,因为它们是水果的子类。这就是为什么它没有抛出异常,这种行为是OOP的基础之一:它被称为多态。 如果数组被声明为一个苹果数组,并且你试图在里面添加水果(与你的情况相反),那么将抛出一个异常:因为你可以隐式地只从一个子节点转换为父节点(从父节点到子节点的转换应该是显式)。