假设我们有以下计划:
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); }
}
}
抛出表示已尝试存储错误 对象类型到对象数组中。
我已阅读here
创建数组时,它会记住它要存储的数据类型。
如果数组记住它包含的数据类型,则意味着它知道它包含的数据类型。但是我发布的代码片段是正确编译的,所以在编译时,数组显然不知道包含什么类型。
我的问题是:
为什么ArrayStoreException
仅在运行时抛出?
编译器缺少哪些信息才能意识到这种分配是不可能的?
ArrayStoreException
?答案 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}}分配时,它无法知道该数组的实际类型是什么。如果是Apple
或Fruit[]
,那很好。如果它是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的基础之一:它被称为多态。 如果数组被声明为一个苹果数组,并且你试图在里面添加水果(与你的情况相反),那么将抛出一个异常:因为你可以隐式地只从一个子节点转换为父节点(从父节点到子节点的转换应该是显式)。