Not Your Father's Java

While reading David Astels' excellent book Test-Driven Development: A Practical Guide [1], I tripped over the following code snippet in a JUnit test:

Vector movieNames = new Vector() { { add("Star Wars"); add("Star Trek"); add("Stargate"); } };

Not your father's Java, eh?

What's going on here? On the right-hand side of the assignment, I recognize the apparent zero-argument constructor invocation followed by a block to be a declaration/instantiation of an anonymous inner class, which extends Vector. The zero-argument Vector constructor initializes the instance's Vector-ness.

We often see the anonymous inner class construct in Swing-based applications, as one-off instances of event listeners:

fileDialog.addMouseListener( new MouseAdapter() { public void mouseEntered( MouseEvent anEvent ) { // hee-hee!! fileDialog.setBackground( Color.BLACK ); } } );

You might even have spotted anonymous inner class instances which implement Runnable or extend Thread:

new Thread( new Runnable() { public void run() { for ( int i = 0; i < 10000; ++i ) { System.out.println( i ); } } } ).start();

Usually, we create anonymous inner classes to specify behavior in response to a method call; that is, we provide an overridden implementation of one or more methods declared in the implemented interface or extended superclass. So what's the deal with the anonymous Vector subclass above? It doesn't refine any method behavior from Vector at all! What's up with the extra set of curly braces, with calls to add() made within?

We have, my friends, discovered Java's instance initializer. [2]

Remember our old buddy the static initializer? You know--those blocks you can use to initialize static fields or perform other initialization tasks after the JVM loads the containing class?

final class ClassUtilities { private static final Map widenings = new HashMap(); static { Set destinations = new HashSet(); destinations.add( Double.TYPE ); widenings.put( Float.TYPE, destinations ); destinations = new HashSet(); destinations.add( Float.TYPE ); destinations.add( Double.TYPE ); widenings.put( Long.TYPE, destinations ); // ... } }

Well, instance initializers are similar. Like static initializers, instance initializers are executed in the order in which they are declared in source. Whereas static initializers are executed on class initialization, instance initializers are executed when an instance of the declaring class is constructed--just after the applicable superclass constructor, but before the applicable declaring-class constructor.

It turns out that the original intents of instance initializers were 1) to provide a means for initializing instance fields lexically near to the declaration of those fields, as opposed to with = or a constructor parameter; and 2) to allow instances of anonymous inner classes to initialize themselves by executing arbitrary code. However, in my nearly seven years of writing and reading Java code, I hadn't once encountered an instance initializer. I had to refer to the JLS [3] to decipher the code snippet.

So we have an anonymous inner class with an instance initializer that calls its add() method thrice.

Don't Repeat Yourself

This got me thinking: Why bring two relatively obscure Java constructs together in this unholy matrimony? What's wrong with:

Vector movieNames = new Vector(); movieNames.add( "Star Wars" ); movieNames.add( "Star Trek" ); movieNames.add( "Stargate" );

Nothing's wrong with it per se; it certainly gets the job done. Dave's method, obscure though it may be, has some advantages.

Concision

Java's syntax, by curly-braced-language standards, is relatively clean--a tame amount of reserved words, very little cartoon-curse-word punctuation, and so forth. However, sometimes we find ourselves typing a lot more than we might like:

Date date = new Date(); class Cineplex { private List movies = new ArrayList(); public List moviesForGeeks() { List geekMovies = new ArrayList(); for ( Iterator it = movies.iterator(); it.hasNext(); ) { Movie nextMovie = (Movie) it.next(); if ( movie.getTitle().startsWith( "Star" ) ) { geekMovies.add( nextMovie ); } } } }

These kinds of gymnastics often leave me wishing I'd have been a Smalltalk programmer: [4]

| thisMoment | thisMoment := Date now. Cineplex>>moviesForGeeks ^self movies select: [ :each | ( each title findString: 'Star' startingAt: 1 ) > 0 ] | movieNames | movieNames := OrderedCollection with: 'Star Wars' with: 'Star Trek' with: 'Stargate'.

I believe that Dave's method betrays a yearning for the simplicity of the last Smalltalk construct above. Hey, why not? Why repeat the receiver over and over if you don't have to? The big reason in Java, of course, is that the instance initializer is obscure. Once you learn the idiom, however, it is admittedly a great way to reduce typing and redundancy without sacrificing too much in the way of clarity. More on this later...

(I'm going to stay out of the which-language-is-more-readable debate, and instead say that there is plenty of help for curly-brace-language devotees who find Smalltalk confusing.[5])

Emphasis on one-offness

Anonymous inner classes are used for one-off objects that implement behavior from an interface, or extend a class to provide special behavior (in this case, initialization behavior). In this sense, Dave's construct works great: By using an anonymous inner class extension of Vector, he emphasizes that there isn't anything particularly special about the list. It's just a collection of some arbitrary movie names for a test to chew on, once and only once.

Java makes anonymous inner classes available at the cost of generating an extra class at compile time:

// yields something like "pholser.sandbox.test.TestGUI$1" System.out.println( movieNames.getClass().getName() );

In JUnit tests, this is of no great consequence. However, in production code, I wouldn't sprinkle such one-offs around willy-nilly, especially not simple lists.

The making of an idiom

Now, some might say that iterating over a Java collection like so:

for ( Iterator it = aCollection.iterator(); it.hasNext(); ) { // ... }

is no less expressive than (Smalltalk):

aCollection do: [ :each | " . . ." ]

It's about as concise as one can get using just the core Java collections API. It seems to be the prevailing idiom. One language's idioms do not necessarily translate so cleanly into another language--to create self-iterating collections in Java, one would introduce block-type interfaces, typically implemented by anonymous inner classes, not the most beautiful of Java syntactic elements. [6]

Just as with natural language, learning the idioms of a programming language represents important milestones on the road to fluency. Indeed, just as with natural language, one doesn't really "know" the language until one begins absorbing the language's idioms. How does an idiom become an idiom? Programming language idioms spread throughout a developer community via the Internet, in books, by word of mouth, and so forth. Certain constructs and expressions over time become commonly recognized and thus the de facto ways of performing given tasks. Over time we "chunk" the construct and recognize it as a whole without having to parse its parts on every read. So for Java programmers, the for-loop-Iterator idiom becomes the expressive way to enumerate a collection.

The prevailing idiom for initializing a Java list seems to be to instantiate an empty list, and to call add() N times, with N statements, repeating the receiver. Might the anonymous-inner-class-plus-instance-initializer construct be an idiom in the making, or is it, like self-iterating collections, an attempt to inject Smalltalk sensibilities into a language that resists it? If I had been steeped more deeply in Java community lore, would I have flinched at the construct? Has the Java community observed and subsequently rejected the idiom, or are we witnesses to an idiom's nascent acceptance?

Stay tuned...

Endnotes

[1] Astels, David. Test-Driven Development: A Practical Guide, pp. 191-192. Prentice Hall PTR, 2003.

[2] JDK 1.1 Inner Classes Specification.

[3] Gosling, James, et al. The Java Language Specification, 2nd ed. Addison-Wesley, 2000.

[4] Raab, Don. "Smalltalk v. Java: Which is More Productive? A Java Developer Answers the Question."

[5] LaLonde, Wilf. "I Can Read C++ and Java but I Can't Read Smalltalk." Journal of Object-Oriented Programming, February 2000, pp. 40-45.

[6] Langr, Jeff. "Enlightened Java Style." Software Development, March 2002.