Reputation: 809
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?Upvotes: 3
Views: 777
Reputation: 156
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.
Upvotes: 2
Reputation: 1219
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).
Upvotes: -2
Reputation: 50766
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.
Upvotes: 3
Reputation: 1503469
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[]
.
Upvotes: 5