Understanding the closures debate

Does Java need closures? Three proposals compared

1 2 3 4 5 Page 3
Page 3 of 5

Closure conversion

All three proposals allow a certain amount of type compatibility among closures and interfaces types: a closure can be assigned to a variable of a compatible interface type. This is an important feature because it allows use of closures in all places where inner classes are currently used in the Java language. This type compatibility ensures that closures are backward compatible to interfaces and inner classes.

The idea backing this type compatibility is the observation that anonymous inner classes are typically used in situations where an implementation of an interface with just one method is needed. Common examples are callback interfaces such as ActionListener with its actionPerformed method or Runnable with its run method.

For the purpose of closure conversion, an interface type is considered compatible to a closure if it has a single method with a compatible return type and compatible argument types. (Actually this is a slight oversimplification: we are ignoring closures that throw exceptions, in which case the throws clauses must also be compatible.) The compatible interface may have additional methods. As long as the additional methods are inherited from class Object the interface is still considered an interface with a single method. The Comparator interface is an example; it specifies a compare method and an equals method. Because every class inherits a default implementation of equals from Object, the Comparator interface is treated like an interface with a single compare method regarding compatibility to closures.

An example of closure conversion

Consider an example of closure conversion using the Runnable interface, where we assign a closure to a regular reference variable of the interface type Runnable. Currently in the Java language we would define an anonymous inner class in order to provide an on-the-fly implementation of Runnable:

Listing 6. Old-style notation using an anonymous inner class

Runnable r = new Runnable() { public void run() { System.out.println("Hello World."); } };
new Thread(r).start();

In CICE, we basically do the same, but with a more concise syntax. For this reason, it is not surprising that a CICE closure is compatible with an interface variable.

Listing 7. CICE notation

Runnable r = Runnable() { System.out.println("Hello World."); };
new Thread(r).start();

In BGGA and FCM, the compatibility between a closure and an interface variable is not obvious and requires the implicit closure conversion.

Listing 8. BGGA notation

Runnable r = { => System.out.println("Hello World."); };
new Thread(r).start();

Listing 9. FCM notation

Runnable r = # { System.out.println("Hello World."); };
new Thread(r).start();

As you can see, all three proposals permit a substantially more compact notation than old-style Java and the concise notation is considered one of the key reasons for adding closures to the language. In fact, all three allow an even more convenient notation where the closure is defined in-place. This would allow us to eliminate the reference variable entirely and pass the closure literal in lieu of a Runnable variable to the thread constructor, as shown here in BGGA notation:

new Thread({ => System.out.println("Hello World."); }).start();

This is the concise and convenient notation that all three closure proposals strive for and achieve using slightly different rules of syntax.

1 2 3 4 5 Page 3
Page 3 of 5