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:
Why is the ArrayStoreException
thrown only at runtime?
What information are missing to the compiler to realise that that assignment is not possible?
ArrayStoreException
is thrown?
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[]
.
See more on this question at Stackoverflow