Saturday, January 19, 2008

What are closures and what do they mean to Java?

I will try and show an example of what closures can be used for and how this functionality looks like if implemented in the current version of Java. I will be using the Scala programming language to demonstrate a closure. Scala is a brand new language that is running on the JVM. I am by no measure a Scala expert, actually prior to writing this post I've only spent a few hours looking at the language. Therefore if you are a Scala guru, please be gentle if commenting about the code.

Before showing you the code, let's first define what a closure is. According to Martin Fowler: A closure is a block of code that can be passed as an argument to a function call. It's not a new idea, it has been around since Scheme in the 60's. It was usually associated with functional programming and not object oriented programming, but it now exists in many OO languages like Ruby and C# (2.0). So what is that you can do with them that you cannot do in Java now?

Let us take an example of what a closure could be used for. File manipulation is a common function in any language and in many languages it requires resource handling of some sort. Doing file operations can usually be abstracted to: Opening the file, so your stuff, close the file. If you forget to close the file, this might lead to your process running out of file pointers. So let's try and abstract resource management a bit and let's write a method on a file that will let you perform an operation on each line of a file without having to worry about opening and closing the file. The example below is as I said in Scala. One of the very nice features is that you can reuse all existing Java classes, so I'll extend the File class and add a forEachLine method to it. This method allows you to pass a block of code that will be executed on each line of the file. So here is the new class:

package ScalaApplication2

import java.io.File
import java.util.Scanner

class ScalaFile(filename: String) extends File(filename: String) {

def forEachLine(parseLine: (String) => Unit) {

val scanner = new Scanner(this)
try {
while ( scanner.hasNextLine() ) {
parseLine(scanner.nextLine())
}
} finally {
scanner.close()
}
}

def compareTo(other: Any): Int = {
0
}

}


So what is going on here? We've extended the File class and added a new method called forEachLine. It takes on parameter named parseLine and it is defined as a function type which takes one parameter of type String and returns nothing (Unit seems to be the synonym of void). As you can see, it is very easy to define a function as a parameter to a method in Scala and it is equally easy to use it inside the while loop. So now that we've implemented this new method let us look at how it can be used.
package ScalaApplication2

object ScalaMain {

def main(args: Array[String]) = {
val file = new ScalaFile("C:\\testFile.txt")
var charCount = 0
file.forEachLine((line: String) => {
println(line)
charCount += line.length()
})
println("Number of characters in File: " + charCount)
}

}


So here we use the forEachLine method to print each line and count the number of characters in the file. To do this we create an anonymous function and as can be seen it is fairly straight forward. Note that the variable charCount is defined outside the scope of the anonymous function and yet can seamlessly be used inside the code block of the anonymous function. Keep that in mind for later.

Our goal of hiding resource management is reached since opening and closing the stream in encapsulated inside the forEachLine method of the ScalaFile class. Now let us look at how we can do this in Java. To get closure like behavior in Java, we need to use interfaces and anonymous classes. Below is the implementation of the new File class that we will us.

package javaapplication1;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

public class JavaFile extends File {

public JavaFile(String filename) {
super(filename);
}

public void forEachLine(JavaFileCallback callback) {

Scanner scanner = null;
try {
scanner = new Scanner(this);
while(scanner.hasNextLine()) {
callback.parseLine(scanner.nextLine());
}
} catch (FileNotFoundException ex) {
Logger.getLogger(JavaFile.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if(scanner != null) {
scanner.close();
}
}

}

public interface JavaFileCallback {

void parseLine(String line);

}

}



Since we cannot pass functions or methods (at least not in a type safe way) as a parameter to our method we have to use an interface with a callback method. Still our goal is accomplished, the stream is opened and closed inside our forEachLine method. Now let's look at what the caller has to do.

package javaapplication1;

import javaapplication1.JavaFile.JavaFileCallback;

public class JavaMain {

public static void main(String[] args) {

JavaFile javaFile = new JavaFile("C:\\testFile.txt");
final int[] charCount = {0};
javaFile.forEachLine(new JavaFileCallback() {
public void parseLine(String line) {
System.out.println(line);
charCount[0] += line.length();
}
});
System.out.println("Number of characters in File: " + charCount[0]);
}

}


In Java, instead of having an anonymous function, we have an anonymous class that we pass to our forEachLine method. Now we all know that variables from the parent class cannot be accessed inside the anonymous class unless they are declared final. So how do we pass on our charCount variable? Well, we have to use a final object reference which variables we can mutate. In this case, we simply use an array. This adds some overhead but it is the only way to do it.

I've just demonstrated that we can replicate the exact behavior of closures with interfaces and anonymous classes. I will even go as far as saying that anything a closure can do, you can implement using the current version of Java (I will stand by this until proven wrong :-). However, that is not so much the point. This is a very simple example and there are more complex uses of closures which require even more complex code in Java. Therefore, the point is that you can do some fairly complex things with little and simple code in a language that has closures built in. In Java, you can do it but let us be honest it does not look pretty. Yet we do it everyday. Think of how many times you've had to create a runnable or callable to simply pass it on to an execution service. It would be much nicer to simply pass blocks of code around instead of actual classes and interfaces.

This is not intended to add anything new to the debate about closures in Java, just to give you an idea of what they are through a simple example. If you're interested in knowing more about closures and the many problems that need to be resolved if we want them in Java, you should check out Neal Gafter's blog. He is one of the driving forces behind the current proposal for Java and has many more examples and in depth discussions about the pros and cons (let's face it mostly pros, but it's still good reading).

No comments: