Why is the ArrayStoreException a RuntimeException?

[亡魂溺海] 提交于 2019-12-07 12:02:55

问题


Let's say we have the following program:

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); } 
    } 
}

Based on the Java documentation:

Thrown to indicate that an attempt has been made to store the wrong type of object into an array of objects.

I've read here that

When array is created it remembers what type of data it is meant to store.

If the array remembers what type of data it contains, it means that it KNEW the type of data it contains. But the snippet I posted is correctly compiled, so at compile time the array apparently doesn't know what type contains.

My questions are:

  1. Why is the ArrayStoreException thrown only at runtime?

  2. What information are missing to the compiler to realise that that assignment is not possible?

  3. Is there any cases in which such code is correct so no ArrayStoreException is thrown?

回答1:


When array is created it remembers what type of data it is meant to store.

The array "remembers" only what type it actually contains during runtime.

First the array is declared, in this case as an array of Fruit.

Then the array is created, in this case as an array of Apple.

Creation is made during runtime, but the compiler is designed only to verify that the array only is assigned objects of the type it is declared as. There are so many things that can occur during runtime.

Consider the following code:

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();
        }
    } 
}

The code is identical to your except that the fruit[0] is assigned a value by the fruitFactory method. There is no way the compiler can tell if the boolean alt is going to be true or false.

What information are missing to the compiler to realise that that assignment is not possible?

As said - the compiler cannot tell if the assignment is possible or not.

Is there any cases in which such code is correct so no ArrayStoreException is thrown?

Yes, in 50 % of the cases in the code above. You either have to verify that the object assigned is the same as the array or catch the exception.




回答2:


If the array remembers what type of data it contains, it means that it KNEW the type of data it contains.

At execution time, yes... just like at execution time, the type of an object is known:

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

The alternative would be to make very many array assignments implicitly throw a checked exception. That would be awful - similar to making NullPointerException checked.

What information are missing to the compiler to realise that that assignment is not possible?

Arrays are covariant, as you've seen. When the compiler sees an assignment into a Fruit[] of an Apple, it can't know what the actual type of that array is going to be. If it's Fruit[] or Apple[], that's fine. If it's Orange[] it's not. That information is only present at execution time - again, just like the compiler doesn't know whether an expression is definitely not null.

Is there any cases in which such code is correct so no ArrayStoreException is thrown?

Well if you have an array with a compile-time element of a final class, then you can't have any lower variance. So for example:

public void foo(String[] array) {
    array[0] = "x";
}

That can throw exceptions due to array being null or empty, but it will never throw an ArrayStoreException, because String is final. The implementation could never be a SubclassOfString[].




回答3:


It's a runtime exception for the same reason ClassCastException is. It's not always possible to tell at compile time whether the type is what you expect or not.

Consider this example:

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

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

There's no way for the compiler to know what state someCondition will be in when you call getFruits(). Hence the runtime exception.




回答4:


At your case, apple and orange are implicitly casted into fruit, because they are subclass of fruit. That's why it"s not throwing an exception, and this behavior is one of the OOP basics : it's called polymorphism. if the array was declared as an apple array and you try to add fruit inside(the opposite of your case), then an exception will be thrown : because you can implicitly cast only from child to parent(the cast from parent to child should be explicit).



来源:https://stackoverflow.com/questions/38391161/why-is-the-arraystoreexception-a-runtimeexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!