🧱 Arrays · Beginner

Array covariance & ArrayStoreException in Java

String[] is an Object[], so storing an Integer fails at runtime.

🧩 The mysteryThe compiler happily lets you put 42 into an array. Then the program crashes on that exact line. The compiler and the runtime disagree. Who knows something the other doesn't?

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]; // allowed

Arrays 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;   // ArrayStoreException
🔮 Predict it

Your turn

What happens?

Object[] things = new Integer[2];
things[0] = 1;
things[1] = "two";
System.out.println("done");
  1. Prints done
  2. Compile error
  3. Throws ArrayStoreException
  4. Throws 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.

🔮 Predict it

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);
  1. 2
  2. Throws ArrayStoreException
  3. Compile 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.

⚠️ The trap

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];   // fine

Arrays vs generic lists

✗ Arrays: caught at runtime
Object[] objs = new String[1];
objs[0] = 42; // crashes when run

Compiles, then throws ArrayStoreException in production.

✓ Generics: caught at compile time
List<Object> list =
    new ArrayList<String>(); // error

Generics are invariant: List<String> is NOT a List<Object>, so the mistake never compiles.

💼 In the real world

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

  1. String[] IS-A Object[] (covariance)
  2. Every array knows its real element type at runtime
  3. Storing the wrong type throws ArrayStoreException
  4. Generics are invariant: List<String> is not a List<Object>, so that mistake is caught at compile time
🤯 Did you know?

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);
  1. Throws ArrayStoreException
  2. 2
  3. 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");
  1. done
  2. Compile error
  3. Throws ArrayStoreException
  4. 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.

Next: sum, max, search, count, reverse. Five tiny loop patterns that solve most array problems.