Rails-style creators in Java, or, how I learned to stop worrying and love anonymous inner classes
Let’s begin with a well-known problem: creating a
Map
that is to be pre-populated with a few values. Here is some naïve code that creates a Map and then puts two values into it:
final Map<String, Object> mm = new HashMap<String, Object>();
mm.put("foo", "Strive To Finish Up");
mm.put("bar", "Public House");
Although this works, the problem with the naïve code is that it does not say with conviction that the two
put
calls are part of the creation of the
Map
. If you move code around, it’s easy to drop a line. This code is
literal, but it is not
literate.
Most Java programmers
1 are familiar with the Map Initializer Idiom:
final Map<String, Object> m = new HashMap<String, Object>() {{
put("bar", "saloon");
put("bash", 7);
}};
This creates a new
Map
and populates it with two members. This is a big improvement. The initialization is clearly embedded in the line of code that creates the Map. Thanks to a small
syntax hack, it is (by Java standards) concise.
If you’re into the details, it actually creates a new anonymous inner class that extends
HashMap
. The inner class doesn’t add any new instance members, but it does contain an initializer: code between braces is run when instances are initialized. That’s why there are double braces: the outer set delineates the body of the anonymous inner class, the inner set delineates the initializer block.
Note that your new Map isn’t exactly equal to any HashMap, because it has a different class. That
sometimes matters.
POJOsMany Java programmers labour with applications where the original architect wielded the Golden POJO Hammer: absolutely everything in the application looked like a POJO nail. I work with one such code base: it is obviously a traditional Procedural application. Getting anything done involves calling a global procedure and passing in a bunch of arguments.
The original architects worked for one of those Global Services companies, and you can’t charge hundreds of dollars an hour
2 writing programs that our grandparents would recognize from the sixties. So to add a layer of
OO, they turned every procedure call into a method, so a simple call like:
final BigDecimal balance = AccountModule.getAccountBalance("12345-678");
became:
final BalanceDoer doer = new BalanceDoer();
doer.setAccountNumber("12345-678");
doer.doit();
final BigDecimal balance = doer.getBalance();
(The nice thing about using this “OO Style” is that you get named parameters and multiple return values. I shall leave it to the static type checking enthusiasts to point out the drawbacks of this approach.)
Well, we can use the same initializer idiom with POJOs like
BalanceDoer
(although you may have to move
doit()
outside of the initializer if you need checked exceptions):
final BalanceDoer doer = new BalanceDoer() {{
setAccountNumber("12345-678");
doit();
}};
final BigDecimal balance = doer.getBalance();
A Little Java, a Few Patterns is brought to you by the authors of The Little Schemer, a book that teaches recursion and first-class functions: it is no surprise to discover that their book about Java eschews the same old, same old Hello World approach in favour of teaching deep ideas about Object-Oriented design and Pattern-Oriented design using Java as the language of instruction.
Again, you get a more literate snippet of code: the initialization and invocation all belong together, and you have put them together. If you move this around, you won’t drop an important line by mistake. And you get to save some keystrokes if there are a lot of fields to initialize.
If you dare to use
protected
fields in POJOs (this is heresy to the “lock everything down so that other programmers subclassing your stuff can’t actually change anything” zealots), you can even write
accountNumber = "12345-678";
in the initializer instead of using a setter.
I prefer that form, it looks a lot more like what you are trying to say: this field has this value for this
doit
call. That may be a matter of taste, or I may be overlooking something important.
Remember, your new object is an instance of an anonymous inner class, not of the original POJO class. This matters greatly if you test for class membership with
equals
instead of substitutability with
instanceof
.
What does this have to do with Rails?This is where we could go off on a long essay about how using things like Lisp, Smalltalk, and Ruby on Rails opens your mind to possibilities. That when you get used to
MyModel.create(:foo => 'Shove That Fish Under')
, you look for ways to make your Java code easier, and so forth.
But honestly, you knew that already, didn’t you?
Update: Writing programs for people to read.
- Where by “most,” I mean those who haven’t been hiding under the “Java-programs-must-only-contain-approved®-design-patterns: all-other-idioms-are-considered-hard-to-read-by-mediocre-colleagues” rock.
In my experience, that is the majority of good programmers. If reading blogs convinces you otherwise, remember that many of the best people are too busy building good stuff to write about building good stuff.
Update: A commenter expressed some surprise that this is a well-known idiom. Have a look at Double Brace Initialization.
The other Java people, the ones with time on their hands, or the ones that consider themselves “too good to program,” are the ones that write posts explaining why Java programs must be dumbed down to the lowest common denominator. I also blog. Draw your own conclusions from the evidence.
- Not this company, although that’s an amusing post.
Labels: java