2

This works:

Object[] array = new Object[3];  
array[0] = "ddd";  
array[1] = new Integer(12);  

This doesn't: (crashes at new Integer)

Object[] array2 = new String[3];  
array2[0] = "ddd";  
array2[1] = new Integer(12);  

I've read about covariance but still can't understand the underlying technical reason the second code example is prohibited, or why an ArrayStoreException is thrown. What is essentially the difference between an array of Object references and an array of String references?

I understand that in the second example, the array gets instantiated with the intention of adding strings to it, but still, something doesn't click logically. Can somebody explain it in simple terms?

4 Answers 4

3

Object references can hold any kind of Object, while String references can hold only Strings -- and the JVM will check that at runtime. There really isn't anything more to it than that.

Your second example actually points up a flaw in the design of the Java language. It's obviously bad that this exception can happen at runtime! The generics feature was designed slightly differently, so that you can't have the same problem with generic collections. A String[] is-a Object[], regrettably; but a List<String> is not a List<Object>

Sign up to request clarification or add additional context in comments.

1 Comment

Yes, and that's in my opinion one of the main reason to favor ArrayList over array
1

Let me make a small change to your second example to illustrate what this is really about:

String[] array1 = new String[3];
Object[] array2 = array1;
array2[0] = "ddd";  
array2[1] = new Integer(12);  // this one

Lets assume that the flagged statement worked. (It doesn't ... but lets assume ...)

Now what happens if I do this?

String wot = array1[1];  

That assignment statement should be statically typesafe because array1 is declared as a String array. But it certainly IS NOT dynamically typesafe! In short, we've just broken Java static typing in a pretty fundamental way.

Java avoids this breakage is by doing a runtime type check as part of the flagged statement, and not allowing a non-String to be assigned into an element of the String[] object.

2 Comments

So it's probably better to disallow adding to prevent other problems like ClassCastException further on?
@Sjoerd - yes. Can you imagine what Java would be like if execution of a statically type-safe assignment or expression could result in a ClassCastException ?
1

In:

Object[] array2 = new String[3];  

The type of the reference is Object[], but the type of the actual object that the reference is referring to is String[].

When you attempt to assign (in your case) an Integer type object to the array, the reference is dereferenced and the actual object is checked. In your second case the type of the array object does not match the type of the object being inserted.

This, of course, is only discoverable at runtime, since the compiler can not be sure exactly what object array2 will be pointing to once the program executes (technically it could in this trivial program, but not in the general case).

Comments

1

The simple definition of covariance: if there is a generic container class with type parameter Container<T>, and X is a subclass of T, then Container<X> is a subclass of Container<T>.

The classic example is a List. If lists were convariant in Java, then List<String> would be a sublcass of List<Object>, because String is a subclass of Object. But lists are not covariant in Java. Arrays are covariant.

Covariance is a useful idea, but can lead to problems in certain situations.

String[] stringArray = new String[1];
Object[] objectArray = stringArray;
objectArray[0] = new Integer(4);
System.out.println("value=" + stringArray[0]); // what should this do?

Because we can assign objectArray = stringArray (because it is covariant), we can then assign any type of object to objectArray[0], which violates the 'contract' that you have when you declared it a String[]. This can't be caught by the compiler. Java avoids this situation by:

  1. Throwing an ArrayStoreException when you try to assign a value which doesn't match the defined type of the array (line 3 above)
  2. Making other classes invariant.

Arrays are covariant in order to generic allow sorts and searches to be written. See Arrays#sort().

Languages such as Scala allow the programmer to choose whether a container class is covariant, contravariant or simply invariant.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.