Are you sure to understand Java arrays?

Justin Dekeyser
Javarevisited
Published in
5 min readJun 7, 2020

Java arrays are the most primitive way of aggregating elements. Yet, are you sure to really understand how they work and how to use them? You may be surprised…

Array represent rows of stuffs

Java arrays: the fundamentals

Java arrays are no new feature of the language. To review the basis, let’s recall that array-types are signalized using the brackets [ ] and created using the curly brackets { } , in a quite straightforward way:

String[] myArrayOfStrings = { "Hello", "World" };

The above is syntactic sugar for the more verbose form (which we are going to stick on in the present text)

String[] myArrayOfStrings = new String[] { "Hello", "World" };

Accessing elements in arrays is expected to be super fast, because of JVM optimization. In comparison with languages like Rust, array lengths is not part of the array type: the type of our array is String[] without information about its length. However, the length of an array is immutable: an array cannot change its size.

In order to change the size of an array, you need to perform a two-steps operation: first create an array with the expected new size, then copy the source in the target:

String[] myArrayOfStrings = ...;
String[] withAugmentedSize = new Steing[4];
System.arraycopy(myArrayOfStrings, 0, withAugmentedSize, 0,
myArrayOfStrings.length);

Array variance

In the above example, the array of strings

new String[] { "Hello", "World" }

is stored in a variable of type String[] . One could wonder in what kind of variable we can store our array of String.

The good news is that array-types are covariant. This means that if String is a subtype of Object , then you can store some String[] in a variable of type Object[] :

Object[] myArrayOfStrings = new String[] { "Hello", "World" };

As usual, you should note two things:

  1. Using the variable myArrayOfStrings does not allow you to perform Stringoperations on the entries, as they are types as Object even though they actually are String
  2. Creating the array as using new Object[]{ ... } also looses the information that the entries are actually strings.

Why are arrays covariant? Well, this may be understood by looking at the arrayCopy method. If array types were invariant (as generic types in Java), it would mean that you would require as many versions of arrayCopy as array types.

And since there exists one array-type for each type in the Java ecosystem, you could not code the arrayCopy method. You could argue by some generic-type argument but do not forget that at that time, Java had no generics! (They appeared in Java 5 only).

What could go wrong?

Let’s inspect with more attention the following two codes:

Object[] myArray1OfStrings = new String[] { "Hello", "World" };

versus

Object[] myArray2OfStrings = new Object[] { "Hello", "World" };

In both cases, the variables myArray_OfStrings aretyped as Object[] . This means that an end-user receives some Object when accessing any entry of the array:

Object x = myArray_OfStrings[0]; // works
String x = myArray_OfStrings[0]; // does not work
myArray_OfStrings[0] instanceof String; // true
String x = (String) myArray_OfStrings[0]; // works

But there is another, subtle difference. What happens if we know try to mutate our entry with another object:

myArray1OfStrings[0] = new Object(); //java.lang.ArrayStoreException
myArray2OfStrings[0] = new Object(); //works

What the hell happened? Here is the trick: the variable myArray2OfStrings references an array that has been created as an array of Object , and this array contains String instances. The array as an object still knows many things: its length, but also its type. In other words, the instance referenced by myArray2OfStrings knows that it’s an array of Object and as such, it does not crash if you plug another general object in one of its entries.

On the contrary, the instance referenced by myArray1OfStrings is an array that has been created as a String[] array. It also knows its length and its type, and it knows that the elements it should contain are expected to be String instances. Hence, trying to plug some general object in one of its entries results in a ArrayStoreException : you cannot store an Object in an array of String.

But what about variance?

You should be obfuscated to learn that: what about the covariance we have seen above? If String[] is a subtype of Object[] , we should be able to store Object[] in it.

Well, in fact there is another subtle thing. The point is that String[] is assignable to Object[] , which means that you can reference an instance of String[] in a variable of type Object[] .

The instanceof operator will also behaves nicely, because if mainly check assignability.

But you should pay attention that String[] is no subclass of Object[] . A quick test shows that:

String[].class.getName(); // [Ljava.lang.String
Object[].class.getName(); // [Ljava.lang.Object
String[].class.getSuperclass().getName(); // java.lang.Object

In other words, String[] is no subclass of Object[] although it’s a subtype. In particular, you can store a String[] in an Object[] , but you cann’t perform any Object[] operation on it (in particular, you can mutate the entry as you want).

Well OK, but who cares?

You may think that the above examples are really weird, and no one would never do that. Well, don’t be so confident!

Assume you have some interface Foo and one implementation FooImpl :

public interface Foo {}class FooImpl implements Foo {}

You then expose a Foo array (and no FooImpl , off course!) but as you need to perform first custom operations on it, you write this:

Foo[] getAllFoos() {   FooImpl[] exposedFoos = new FooImpl[] { ... };   performCustomOperationsOnFoos (exposedFoos);
// this required the array to be FooImpl[]
// for achieving custom goals
return exposedFoos;
}

Now if a client also wants to mutate the array for achieving his goal, he would end in some ArrayStoreException !

There is no good practice in the community: should you first copy the array for exposing a fair Foo[] or should it be the responsibility of the client to copy if necessary?

The least we can say (and please, without entering in the debate of primitive arrays) is that everyone should be aware of those subtle rules governing the world of primitive arrays!

--

--

Justin Dekeyser
Javarevisited

PhD. in Mathematics, W3C huge fan and Java profound lover ❤