
Imagine a world without boxes, where everything is scattered without any organization. I don't about you, but I find it impossible to even conceive of such a world. We probably don't give it much thought but boxes are the glue that holds our life together. Similarly, programming languages also have objects that act as containers of other objects, and like boxes, they too form the bedrock of computing.
Almost every program has to deal with multitudes: multiple windows, multiple accounts, multiple invoices, multiple line items and multiple all-sorts-of-things. Containers like arrays make it possible for us to store multiple things together and then process them in a loop or individually.
We have already written about generics, here and here, but there is a surprising fact about generics and arrays in Java. Read ahead to find out two lesser known but interesting things about Java arrays, which every programmer should know.
Java arrays are covariant
By this we mean is S is a sub-type of T, then S[] will be a sub-type of T[], or in other words an array of S will be a sub-type of an array of T. Bear in mind that arrays are separate types as far as the type system goes; String[] and String are totally different types.
From a coding perspective covariance in arrays allows us to program to interfaces while using arrays, as shown in Code Example 1 below.
Number nums[] = new Integer[10];
Code Example 1
It also allows us to write general method signatures even when they have arrays as parameters as shown in Code Example 2 below.
public class ArrayCovariance {
public static void main(String args[]) {
Integer someNums[] = new Integer[100];
print(someNums);
}
public static void print(Number nums[]) {
for(Number n : nums) {
System. out.println(n);
}
}
}
Code Example 2
This feature however comes with a little security risk, opening up a possibility of sneaking in incorrect objects into unsuspecting arrays.
Object o[] = new String[10];
o[0] = new Double("3.333" );
Code Example 3
In Code Example 3, even though the type of the variable 'o' is Object[], the actual instance is of type String[]. Even though syntactically correct, semantically it is incorrect to put any object other than a String into this array. That is exactly why this code compiles fine, but try running it, and you will see an exception:
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Double
at com.programmr.percolation.ArrayCovariance.main( ArrayCovariance.java:11)
Console Output 1
Java's runtime is not going to simply take the compiler's word and allow your code to stuff anything into that array. It actually checks those object types (in the example above - Double) and makes sure they match with the type of the array that was instantiated (in the example above - String). If the types don't match, a java.lang.ArrayStoreException is thrown. This runtime feature adds safety to the power of covariance in Java arrays.
Java arrays do not support generic type information
Which, put very simply, means we cannot write code like this.
public void print(T[] t) {
}
Code Example 4
Arrays of generic types are not allowed. What's more, array types cannot even contain any kind of type information. We cannot write code like this either.
List<String>[] t = new ArrayList<String>[10];
Code Example 5
The compiler will stop you in your tracks with an error message - Cannot create a generic array of List<String>
So why doesn't Java allow arrays to have generic type information ? Aren't arrays as deserving of the type safety, supported by generics, as are collections ? They are, but covariance gets in the way.
Java array's support of covariance and the generics' implementation using type erasure don't play well together. Let's dig a little deeper. We have already discussed why the Java runtime needs to check the type of every object that is added to an array. However, this is possible only if an array's type information is present in the compiled bytecode. Unfortunately generics are implemented with type erasure, which means an array of ArrayList of String objects (ArrayList<String>[]) is represented as an array of List objects (List[]) in the bytecode. Unfortunately because generic type information is non-existent in the bytecode, the runtime system will not be able to verify if the correct objects are inserted into the array. Therefore Java does not support any generic type information in arrays to prevent disruption of the type system.