Java 为什么ArrayStoreException是RuntimeException?
假设我们有以下程序:Java 为什么ArrayStoreException是RuntimeException?,java,arrays,exception,exception-handling,runtimeexception,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 {
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); }
}
}
根据:
抛出以指示已尝试存储错误的
将对象类型转换为对象数组
我读过
创建数组时,它会记住要存储的数据类型
如果数组记住它包含的数据类型,则表示它知道它包含的数据类型。但是我发布的代码段编译正确,所以在编译时数组显然不知道包含什么类型
我的问题是:
ArrayStoreException
ArrayStoreException
Object x = "foo";
// The compiler won't let you call x.length() here, because the variable
// x is of type Object, not String.
另一种方法是使很多数组赋值隐式抛出一个选中的异常。这将是可怕的-类似于检查NullPointerException
编译器缺少哪些信息来认识到该赋值是不可能的
正如您所看到的,数组是协变的。当编译器看到一个赋值到苹果的果实[]
中时,它无法知道该数组的实际类型。如果是水果[]
或苹果[]
,那就好了。如果它是Orange[]
则不是。这些信息只在执行时出现——同样,就像编译器不知道表达式是否绝对不是null一样
是否存在这样的情况:这样的代码是正确的,因此不会引发ArrayStoreException
如果有一个数组,它的编译时元素是final类,那么方差就不能再低了。例如:
public void foo(String[] array) {
array[0] = "x";
}
由于array
为null
或为空,它可能会引发异常,但它永远不会引发ArrayStoreException
,因为String
是最终的。该实现永远不能是子类字符串[]
它是运行时异常,原因与ClassCastException
相同。并不总是能够在编译时判断类型是否是您期望的类型
考虑这个例子:
void method1() {
Fruit[] fruits = getFruits();
fruits[0] = new Orange();
}
Fruit[] getFruits() {
if (someCondition) {
return new Apple[5];
} else {
return new Orange[5];
}
}
当您调用getFruits()
时,编译器无法知道someCondition
将处于什么状态。因此出现了运行时异常。在您的例子中,apple和orange被隐式地浇铸到水果中,因为它们是水果的子类。这就是为什么它不会抛出异常,而这种行为是OOP的基础之一:它被称为多态性。
如果数组被声明为apple数组,并且您尝试在其中添加水果(与您的情况相反),那么将引发异常:因为您只能隐式地从子级转换到父级(从父级到子级的转换应该是显式的)
创建数组时,它会记住它要存储的数据类型
商店
数组在运行时只“记住”它实际包含的类型
首先声明数组,在本例中是一个水果数组
然后创建数组,在本例中为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();
}
}
}
类水果{}
苹果类扩展水果{}
类{}
橙色类扩展水果{}
公共类主{
公共静态void main(字符串[]args){
水果[]水果=新苹果[10];
布尔alt=(Math.random()<0.5);
试试{
水果[0]=水果工厂(alt);
}catch(异常e){System.out.println(e);}
}
私人静态水果工厂(布尔苹果){
如果(苹果){
返回新苹果();
}否则{
返回新的橙色();
}
}
}
该代码与您的相同,只是水果[0]由水果工厂方法赋值。编译器无法判断布尔alt是true
还是false
编译器缺少哪些信息来实现这一点
任务不可能吗
如上所述-编译器无法判断赋值是否可行
是否存在此类代码正确的情况,因此没有
是否引发ArrayStoreException
是的,在上述代码中有50%的情况下。您必须验证分配的对象是否与数组相同,或者捕获异常。从编译器的角度来看,这些语句是正确的,因为数组在Java中是协变的。请参阅。请注意,布洛赫认为数组的协变性是首选列表而非数组的原因(有效Java第二版,第25项),以一个与本问题中所述非常相似的示例作为开头。