Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 232 posts at DZone. You can read more from them at their website. View Full User Profile

A Java Language Quirk

11.09.2010
| 7029 views |
  • submit to reddit

The boys at Sun made really an amazing thing when designing Java. I mean, everyone is cursing it now, predicting Java is dead because of its verbosity and such but could those persons have done better?  I doubt it. Just consider Java ‘s penetration in the enterprise… Verbosity enforces readibility, a nice feature in the enterprise where people move from position to position, in and out.

However, I have stumbled upon a troubling quirk. Most Java’s nuts and bolts are enforced at compile time, and erased at runtime. If nothing wrong gets compiled, there’s no reason to check at runtime. The glitch I found compiles fine but throws an exception at runtime.

Consider the following piece of code:

Object[] objects= new String[1];

objects[0] = new Integer(5);

It compiles juts fine but if you execute it, you will be faced with an strange java.lang.ArrayStoreException… Which statement is wrong? Obviously, the first one. How can an array of strings can be considered an array of objects?

Now, I’m not too fond of generics and the convoluted way they are implemented in Java 5 but this particular quirk is not possible with them. None of the two following snippets will compile:

// Type mismatch: cannot convert from ArrayList<String> to List<Object>
List<Object> list = new ArrayList<String>();
List<? extends Object> list = new ArrayList<String>();

// The method add(capture#1-of ? extends Object) in the type List<capture#1-of ? extends Object> is not applicable for the arguments (Integer)
list.add(new Integer(3));

For those interested in theory, this behaviour where an array of String can be seen as an array of Object is called covariance. Funny to say it, but Java 5 generics are actually an improvement over array whereas covariance is concerned!

Note that Scala makes arrays nonvariant at compile-time and thus correctly handles the problem:

// type mismatch;  found   : Array[String]  required: Array[java.lang.Object]
var array:Array[Object] = new Array[String](0)

From http://blog.frankel.ch/a-java-language-quirk

Published at DZone with permission of Nicolas Frankel, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Jörg Buchberger replied on Tue, 2010/11/09 - 3:20am

Nicolas,
I'm interested what exactly you consider strange about the ArrayStoreException? It needs to be there, since the compiler has no way to guard against it, if the two lines of code from your example are not compiled as part of the same code block. (They could just as well exist in two different methods, classes or projects, but still be linked together at runtime.)

This is not really different from a NullPointerException - do you consider that a strange language quirk, too?
Object[] objects = new String[1];
objects[0].toString(); // you will be faced with a NullPointerException here

If you need type safety when working with arrays, do this:
String[] strings = new String[1];
strings[0] = new Integer(5); // believe me, the compiler will tell you this line is wrong

Ricky Clarkson replied on Tue, 2010/11/09 - 3:46am

The compiler could guard against it if it did not treat String[] as a subtype of Object[]. That's the real problem. I think Joerg could do with reading the original post more slowly instead of reacting to it. In any case, without generics and without String[] being a subtype of Object[], Java 1.0 would not have been able to provide any general way of doing something to an array, apart from via reflection.

Andrew McVeigh replied on Tue, 2010/11/09 - 4:31am

with all statically typed languages, there's a limit to what can be statically checked.  you can push a lot of stuff into the static checking by making the type system very powerful, but the language ends up being difficult to understand (i.e. scala).

in this case, they've chosen to push the checking of array types into the runtime.  as ricky notes, the problem could be fixed by making it so that String[] isn't a subclass of Object[] but then you'd not be able to pass in String[] even to methods which only require reading of Object[] arrays.  Generics does actually fix this where List<String> is  actually a subclass of List<? extends Object> (read only) but not of List<Object>.

It's all back to the old covariance (less type safety, more intuitive) versus contravariance (more type safety, less useful) arguments.

p.s. null pointers are actually highly undesirable (http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake) and it is possible to design a language which can statically prove they can't occur.  however, programming in such a language can so draconian that you might wish to allow null pointers and wear the cost.

Ricky Clarkson replied on Tue, 2010/11/09 - 4:50am

Use-site covariance annotations are less intuitive but safe. Declaration-site covariance annotations are more intuitive and just as safe. Witness C# 4's <in T>, <out T>, etc.

Andrew McVeigh replied on Tue, 2010/11/09 - 6:03am

yes, the <in T> <out T> stuff is nice.

just saw the Gosu JVM language: http://gosu-lang.org/intro.shtml

it's a language with covariance applied to generics, as per the following comment.  certainly i'd argue that covariance is generally what people want, and the cost of losing the static safety is often minimal.

 Gosu supports a simplified version of generics. Generics are a way to abstract the behavior of a class to work with different types of objects, but to still retain type safety. There are no wildcards, and generic types are covariant, like Java arrays, which is usually what you want.

 

Stephen Colebourne replied on Tue, 2010/11/09 - 9:49am

Fantom http://fantom.org also allows conversion between a list of obects and a list of strings without hassle. Generally, variance is little understood, and the solutions in Java and Scala are both more complex than the neat simplicty of arrays.

 I also have some plans for enhancing Fantom in this area. 

Jason Kilgrow replied on Tue, 2010/11/09 - 9:55am

"Obviously, the first one. How can an array of strings can be considered an array of objects?" Ummm....let me source the API...hmmm...yep. It looks like String still extends Object. So...what's the problem with Object[] objects= new String[1]; ? The problem with the first example is that you have set up your memory blocks on the heap to hold an array of String types and you are trying to shove an Integer type into it. How big is one String object? It depends on what you initialize it with. How big is an Integer type? 32 bytes. So, while the String object would "probably" fit into that initialized memory, the jvm can't guarantee the type safety so it throws an exception. There are languages that don't enforce type safety as strictly (perl, javascript, etc) but, java isn't perl. :) It was written the way it was for a reason and that reason was so that you can't mix types as in the first example. In doing so, the jvm further guards against memory errors and other Really Bad Things at runtime.

Andrew McVeigh replied on Tue, 2010/11/09 - 10:20am

The problem with the first example is that you have set up your memory blocks on the heap to hold an array of String types and you are trying to shove an Integer type into it.

Huh?  The array is just an array of references, and each array element can hold either a String or an Integer (or any other object) reference. The references are stored in the array, the objects live on the heap.

Of course, if you put a primitive int into a list, it will be autoboxed into an Integer object.

wayne kaneshiro replied on Tue, 2010/11/09 - 11:50am

 On vim I get the following errors:

Sample4_2c.java:43: ']' expected
private objects[0] = new Integer(5);
^
Sample4_2c.java:43: ';' expected
private objects[0] = new Integer(5);
^
Sample4_2c.java:43: illegal start of type
private objects[0] = new Integer(5);
^
Sample4_2c.java:43: <identifier> expected
private objects[0] = new Integer(5);
^
Sample4_2c.java:43: ';' expected
private objects[0] = new Integer(5);
^
Sample4_2c.java:43: illegal start of type
private objects[0] = new Integer(5);
^
Sample4_2c.java:43: <identifier> expected
private objects[0] = new Integer(5);
^
Sample4_2c.java:43: ';' expected
private objects[0] = new Integer(5);
^
8 errors

# javac -version
javac 1.6.0_18

Even und using objects[]; I get

Sample4_2c.java:43: <identifier> expected
private objects[] = new Integer(5);

1 error

 Could be the sophisticated IDE is giving sophisticated problems.

Andrew McVeigh replied on Tue, 2010/11/09 - 12:13pm in response to: wayne kaneshiro

> private objects[0] = new Integer(5);

 this is not Java.  i presume you want something like:

      private Object[] objects = {new Integer(5) };

but I'm just guessing.  what are you trying to do?  where did your code come from?

wayne kaneshiro replied on Tue, 2010/11/09 - 12:34pm in response to: wayne kaneshiro

 

Oops!

 Tried again and your right!!

It does compile in the main. 

Jörg Buchberger replied on Wed, 2010/11/10 - 4:33am

@Andrew,Ricky,Stephen,Jason:

Thanks to your thoughtful comments, I'm somewhat inclined to change my voting from Dislike to Like, just because your input on the topic is so valuable. However, I stand by my comment - even though Ricky is right in the sense, that it would have been better to spend more time reading (and especially writing) to get a more respectful tone across.

Cheers!

p.s. Und nix für ungut, Nicolas! No harm meant.

Cosmin Mutu replied on Thu, 2010/11/11 - 2:48am

Object[] objects = new String[1]; ... yeah, String extends Object

objects[0] = new Integer(1);  ... yeah, you can put an Integer on an Object array

I think this post is all about "Note that Scala makes arrays nonvariant at compile-time and thus correctly handles the problem".

Yes, let`s all praise SCALA for beeing more rigid than JAVA on this "problem".

Nicolas Frankel replied on Thu, 2010/11/11 - 4:31am in response to: Cosmin Mutu

Dear Cosmin,

The point of this article is that though much of Java has compile time checking, this particular issue has not. I just make readers aware that Scala handles the problem, not more, not less.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.