Quality code starts with the details of the language
The code I write in Ruby is higher quality than the code I write in Java. Why? Because the language supports better abstractions. It lets me express a problem once, and reuse it many places. The strength of an environment comes from the quality of the libraries, and the qualities of the libraries comes from the features of the language. In this post, I will review a proposal to make Java a better language: First-Class Methods. See how much better the code looks with it.
If you are like me, you often read data from a file or a database connection, and then you have to write boilerplate code in a finally
block. This means lots of repeated code. To make the code correct, you actually need this monstrosity:
/** Please don't read this code, it is just here to show how unfair life can be. */
public static void theOldWay(String[] args) throws IOException {
BufferedReader reader = null;
boolean done = false;
try {
reader = new BufferedReader(new FileReader(new File(args[0])));
String line;
while ( (line = reader.readLine()) != null) {
System.out.println("This is a line:" + line);
}
done = true;
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
if (done) throw e;
}
}
}
Having to write this sort of code once isn’t too much to ask. Having to repeat it for every time you want to read a file is an affront to sanity. The Spring framework introduced JdbcTemplate and similar constructs which really cut down on the room for error, but there’s still a lot of code. But look now: Here is how the code works with the First-Class Methods prototype:
public static void main(String[] args) throws IOException {
forEachLine(new File(args[0]), #(String line) {
System.out.println("This is a line:" + line);
});
}
I have implemented the forEachLine
pretty much like the original, ugly code. The important thing to note here is that I only have to write it once. This allows us to hide more of our boilerplate code in libraries. This is the power of closures! But wait, there is more. The #1 reason I prefer the FCM proposal over rival proposals for closures (BGGA, CICE) is the fact that it is simple to explain and works well within the expected behavior of the Java language. But in addition to this, First-Class Methods give us a much missed feature of the Java language: Compile time reflection. To understand this, let me explain the name “First-Class Methods” first. The proposal is really to give Java Methods and Fields first class status in the language. The programming language geek term “first class” basically means something that can be passed to and returned from methods. What does this mean in practice? Well, have you ever felt uneasy about writing code like this:
public static void theOldWay() throws
NoSuchMethodException, IllegalAccessException, InvocationTargetException
{
OriginClass origin = new OriginClass();
origin.setBar("origin mapped as property");
TargetClass target = new TargetClass();
// We now want to copy the "bar" property of OriginClass to the "foo" property of the TargetClass
Method fromMethod = OriginClass.class.getMethod("getFoo");
Method toMethod = TargetClass.class.getMethod("setFoo", String.class);
toMethod.invoke(target, fromMethod.invoke(origin));
System.out.println(target.getFoo());
}
Well, you should feel uneasy about it, because that code contains a typo! The comments says to use getBar
, but the code mistakenly uses getFoo
. Why can’t I get the compiler to check this? Well, with First-Class Methods, I can. This code gives a compiler error:
public static void theNewWay() throws
IllegalAccessException, InvocationTargetException
{
OriginClass origin = new OriginClass();
origin.setBar("origin mapped as property");
TargetClass target = new TargetClass();
// We now want to copy the "bar" property of OriginClass to the "foo" property of the TargetClass
Method fromMethod = OriginClass#getFoo();
Method toMethod = TargetClass#setFoo(String);
toMethod.invoke(target, fromMethod.invoke(origin));
System.out.println(target.getFoo());
}
This allows me to construct easy to use libraries for mapping properties between objects. For example, how do you like the following?
public static void withLibrarySupport() {
OriginClass origin = new OriginClass();
origin.setBar("bar on origin");
TargetClass target = new TargetClass();
mapField(origin, OriginClass#bar, target, TargetClass#foo);
System.out.println(target.getFoo());
}
Pay special attention to the following fact: This code throws no checked exceptions. None! All error situations are already resolved compile time. See the companion source code in the appendix if you wonder how. I’m really impressed with the First-Class Methods proposal. It solves the lack of closures in Java by letting you pass anonymous methods to other methods. This explains the initially strange syntax of forEachLine(new File(args[0]), #(String line) {...
. #(String line)
means a method without a name that takes a String parameter, just like I used TargetClass#setFoo(String)
to refer to a named method. The #
symbol is what’s being used for the same purpose in JavaDoc references, so it should be easy to remember. And in addition to giving us closures, this proposal also solves the problems of compile time reflection. I have just scratched the surface of what kind of libraries can be created with this technology, which solves real problems without adding much weight to the language. Compared to the monster that is BGGA (“Bracha, Gafter, Gosling and Ahé” closures) and the fart that is CICE (Consise Instance Creation Expression), First-Class Methods is a simple, yet powerful proposal for closures. This is what I want.
Appendix: Trying out FCM for yourself
There is currently a prototype available for FCM. Here is how to use it:
- Download the prototype code from the kijaro project
- Unzip it, say to
C:\FCM-2008-02-25
- When compiling from the command line, use a JDK 1.6 version of
javac
, and call it with like so:javac -J-Xbootclasspath/p:"C:\FCM-2008-02-25\lib\javac.jar" -d . _java files_
. - When running the compiled code, use Java 1.6, and call it like above:
java -Xbootclasspath/p:"C:\FCM-2008-02-25\lib\javac.jar" -classpath . _java class_
.
Here is the source code for my example code in this article, if you want something to get started with: IOClosureTest.java and MethodReferenceTest.java.