Java Peeves

by Charles Miller on July 16, 2002

I got annoyed today and wrote about a few Java Peeves.

Compiler optimizations disguised as language features.

The artificial distinction between primitive types and objects is just that. Artificial. It exists in the compiler and JVM because it's more efficient to deal with the number 4 than an Integer object representing the number 4.

This is broken. The programmer should not be responsible for this sort of optimization, it's the responsibility of the compiler.

The same goes for the array. Because the Array isn't a real object, all of the methods needed to manipulate it are static methods of the java.util.Arrays class, except of course for System.arrayCopy(). On top of that, because primitive types are not objects (and thus arrays of primitive types are not arrays of objects), that interface is cluttered with identical methods for every different primitive type!

And I still don't get the mental gymnastics behind String[] foo = (String[])list.toArray(new String[0]); but it works, so I don't touch it.

But, you say, we need the Array to be special because unlike collections, arrays are keyed to a specific type, instead of just storing Object. To this, I reply that this is broken as well:

Object[] bar = new String[3];
bar[0] = new Integer(1);

You need to be able to cast all objectified array types to Object[] to make it possible to write methods that handle all object arrays. Once cast, the compiler won't pick up you putting the wrong kind of object in the array - you'll get a runtime error instead.

The exception heirachy.

Exception and Error are subclasses of Throwable. So far, so good. RuntimeException is a subclass of Exception. This is the wrong way round. 90% of the time, you don't want to catch RuntimeExceptions, but you do want to catch checked exceptions. So the idiom has to be:

try {
      ...
} catch (RuntimeException e) {
      throw e;
} catch (Exception e) {
      do something
}

This wouldn't be so bad if the exception hierachy below Exception had been better designed, but there are far too many places in Java where you have to catch five different exceptions, none of which share a common ancestor higher than Exception, and all of which you want to do exactly the same thing with.

Lack of blocks/closures

Sure, there's nothing you can do with a closure that you can't do with an anonymous inner class, but the overhead required to write an inner class discourages their use, as is shown by the lack of internal iterators in the Collections API. Hence, whereas in Ruby I'd write "list.each { |thing| doSomethingTo(thing) }", in Java I have to write:

for (Iterator i = list.iterator(); i.hasNext();;) {
     doSomethingTo(i.next());
}

Previously: I Will...

Next: Wed, 17, Jul 2002 01:07:00 PM