Array covariance & ArrayStoreException in Java
String[] is an Object[], so storing an Integer fails at runtime.
Covariance
Arrays are covariant: because String is a subtype of Object, **String[] is a subtype of Object[]**. So a String[] can be assigned to an Object[] variable. The same goes for Integer[] and Number[].
String[] words = new String[2];
Object[] objs = words; // allowed
Number[] nums = new Integer[3]; // allowedArrays remember
Every array knows its real element type at runtime. The compiler only sees the variable's type (Object[]) and allows storing anything. But the JVM checks every store, and putting a non-String into a real String[] throws **ArrayStoreException**.
String[] words = new String[2];
Object[] objs = words;
objs[0] = "hi"; // fine: a String
objs[1] = 42; // ArrayStoreExceptionYour turn
What happens?
Object[] things = new Integer[2];
things[0] = 1;
things[1] = "two";
System.out.println("done");Prints doneCompile errorThrows ArrayStoreExceptionThrows ClassCastException
Show the answer
It compiles, because the variable is an Object[]. But the real array is an Integer[], and the runtime check on the store of "two" rejects it: ArrayStoreException.
A real Object[]
This time the array really is an Object[]. What prints?
Object[] box = new Object[2];
box[0] = "two";
box[1] = 42;
System.out.println(box.length);2Throws ArrayStoreExceptionCompile error
Show the answer
2. A genuine Object[] accepts any object: a String and an Integer (42 is autoboxed) both fit. The check only fails when the real element type is narrower.
Primitives don't play
Covariance only applies to arrays of objects. int is a primitive, so **int[] is not an Object[]**: that assignment is a compile error. (An int[] is still an Object, like every array.)
Object[] a = new int[3]; // compile error
Object o = new int[3]; // fineArrays vs generic lists
Object[] objs = new String[1];
objs[0] = 42; // crashes when runCompiles, then throws ArrayStoreException in production.
List<Object> list =
new ArrayList<String>(); // errorGenerics are invariant: List<String> is NOT a List<Object>, so the mistake never compiles.
In real projects
This is one reason the book *Effective Java* says "prefer lists to arrays" in APIs: generic collections catch type mix-ups at compile time, while arrays only catch them at runtime, possibly in front of customers.
Key takeaways
- String[] IS-A Object[] (covariance)
- Every array knows its real element type at runtime
- Storing the wrong type throws ArrayStoreException
- Generics are invariant: List<String> is not a List<Object>, so that mistake is caught at compile time
Array covariance exists because Java 1.0 had no generics: methods like Arrays.sort(Object[]) needed to accept any object array. Generics arrived in Java 5 and were made invariant on purpose.
Practice questions
What does this print?
Object[] items = new Object[2];
items[0] = "ok";
items[1] = 42;
System.out.println(items.length);- Throws ArrayStoreException
- 2
- Compile error
Check your answer
2. This array really is an Object[], so it can hold a String and an Integer (42 is autoboxed).
What does this print?
Object[] items = new String[2];
items[0] = "ok";
items[1] = 42;
System.out.println("done");- done
- Compile error
- Throws ArrayStoreException
- Throws ClassCastException
Check your answer
Throws ArrayStoreException. The variable's type is Object[], so the compiler allows storing 42. But the actual array object is a String[], and the runtime check on every store rejects an Integer.