Runnable
Runnable
, Callable
, or other closuredo
’ keyword in a new way that fits in with Java syntax.Example:
Runnable r = do(foo, bar) { System.out.println(foo + bar + baz); } |
The compiler generates an inner class $java_lang_Runnable1
that has
(foo, bar)
arguments and
run()
method containing the given code.
The compiler also replaces the above statement with this:
Runnable r = new $java_lang_Runnable1(foo, bar); |
(The surrounding code is shown in the next example.)
This proposal eliminates a lot of clutter, especially when you need aRunnable
that has a constructor with arguments. Many proposals involving new language syntax are nonstarters because a keyword is needed but none of the existing ones is close enough to the meaning. In this case, I think it is fortunate that 'do
' is a perfect fit.
The same syntax could be used for Callable
. The compiler decides whether to create a new Runnable
or a new Callable
simply by the presence of a return
statement with an argument. (See The trouble with Callable
below.)
Note: All examples on this page compile and run using JDK1.5.0. Download them here.
import java.util.concurrent.Callable; class EasyRunnable { public static void main(String[] args) throws Exception { // new EasyRunnable().proposal(); new EasyRunnable().worksLikeThis(); } String baz = " so far."; static void proposal() throws Exception { String foo = "Objections: "; int bar = 0; // Runnable r = do(foo, bar) { System.out.println(foo + bar + baz); } // r.run(); // Callable<String> c = do(foo, bar) { return foo + bar + baz; } // System.out.println(c.call()); } void worksLikeThis() throws Exception { String foo = "Objections: "; int bar = 0; Runnable r = new $java_lang_Runnable1(foo, bar); r.run(); Callable<String> c = new $java_util_concurrent_Callable1(foo, bar); System.out.println(c.call()); } //---------------------------------------------------------------------------- // Code from here to end of class is generated by the compiler private class $java_lang_Runnable1 implements Runnable { $java_lang_Runnable1(String foo, int bar) { this.foo = foo; this.bar = bar; } private final String foo; private final int bar; public void run() { System.out.println(foo + bar + baz); } } private class $java_util_concurrent_Callable1 implements Callable<String> { $java_util_concurrent_Callable1(String foo, int bar) { this.foo = foo; this.bar = bar; } private final String foo; private final int bar; public String call() { return foo + bar + baz; } } } |
The proposal could go further and have the compiler figure out the variables that have to be passed to the constructor. This would eliminate the need for arguments after the do
.
Runnable
and Callable
Runnable
is an easy target for such a syntax proposal because Runnable
has only a single, no-arg method (and Runnable
is in the java.lang
package).
But can we expand this syntax idea to encompass any class or interface?
Consider:
Runnable r = do(foo, bar) { System.out.println(foo + bar + baz); } |
Runnable
is the obvious choice for a default class/interface for closures, so the above is shorthand for this:
Runnable r = do(foo, bar) Runnable { System.out.println(foo + bar + baz); } |
Since Runnable
has only one method, the above is shorthand for this:
Runnable r = do(foo, bar) Runnable.run() { System.out.println(foo + bar + baz); } |
If an interface has only one method, the method need not be named explicitly. The same goes for an abstract class that has only one abstract method.
Now we have some wiggle room to use the closure syntax with other classes or interfaces. Here is some example code:
import javax.swing.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.*; class EasyListener { public static void main(String[] args) throws Exception { EasyListener instance = new EasyListener(); // instance.proposal(); instance.worksLikeThis(); } EasyListener() { textField = new JTextField(); textField.setText("Type the Return key."); JFrame frame = new JFrame(); frame.add(textField); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().setPreferredSize(new Dimension(300, 40)); frame.pack(); frame.setVisible(true); } static final String msg = "Event: "; JTextField textField; //static void proposal() throws Exception { // f.addActionListener(do(msg) ActionListener { System.out.println(e); }); // f.addKeyListener (do() KeyListener.keyTyped { System.out.println(e); }); //} void worksLikeThis() throws Exception { textField.addActionListener(new $$$L1(msg)); textField.addKeyListener (new $$$L2() ); } private class $$$L1 implements ActionListener { public $$$L1(String msg) { this.msg = msg; } private final String msg; public void actionPerformed(ActionEvent e) { System.out.println(msg + "ActionEvent"); } } private class $$$L2 implements KeyListener { public void keyPressed(KeyEvent e) {} public void keyReleased(KeyEvent e) {} public void keyTyped(KeyEvent e) { System.out.println("KeyEvent"); } } } |
Again, we get a massive reduction in clutter, especially when implementing an interface that has multiple methods.
What about method arguments? The closure code can use the declared names of the method arguments, so this:
f.addKeyListener(do() KeyListener.keyTyped { System.out.println(e); }); |
is shorthand for this
f.addKeyListener(do() KeyListener.keyTyped(KeyEvent e) { System.out.println(e); }); |
where the name e
is taken from the declaration of KeyListener
.
Leaving out the argument list makes the code more compact, but it does require that KeyListener.class has not been stripped of its method argument symbols. This shorthand feature also requires a new understanding on the part of API programmers that the declared names of arguments of overridable methods are now part of the API (so they must not be changed). I don’t think this is a terrible thing.
If you choose not to rely on the declared argument names in an API, you have the option to declare your own argument name(s), like this:
f.addKeyListener(do() KeyListener.keyTyped(KeyEvent evt) { System.out.println(evt); }); |
Providing explicit method arguments in the do statement can also serve to disambiguate overloaded methods.
In answer to Martin Fowler’s page about the lack of closures in Java and other languages, I offer the following rendering of his example in a variant of Java embodying this proposal. This example also illustrates use of an abstract class that has a single abstract method.
import java.util.Collection; import java.util.ArrayList; import java.util.LinkedList; // See http://www.martinfowler.com/bliki/Closure.html public class FowlerDemo { public static void main(String[] args) throws Exception { emps = new ArrayList<Employee>(); Employee john = new Employee("john", true, 200); emps.add(john); emps.add(new Employee("joe", false, 40)); System.out.println(managers(emps)); System.out.println(highPaid(emps)); Selector<Employee> highPaid = paidMore(150); System.out.println(highPaid.select(john)); } static Collection<Employee> emps; static Collection<Employee> managers(Collection<? extends Employee> empsArg) { // return do(empsArg) Selector<Employee> { return item.isManager; }.selectAll(); return new $Selector1<Employee>(empsArg).selectAll(); // Works like this } static Collection<Employee> highPaid(Collection<? extends Employee> empsArg) { int threshold = 150; // return do(empsArg, threshold) Selector<Employee> { return item.salary < threshold; }.selectAll(); return new $Selector2<Employee>(empsArg, threshold).selectAll(); // Works like this } static Selector<Employee> paidMore(int amount) { // return do(empsArg, amount) Selector<Employee> { return item.salary > amount; }; return new $Selector3<Employee>(emps, amount); // Works like this } //---------------------------------------------------------------------------- // Code from here to end of class is generated by the compiler private static class $Selector1<T> extends Selector<Employee> { public $Selector1(Collection<? extends Employee> c) { super(c); } public boolean select(Employee item) { return item.isManager; } } private static class $Selector2<T> extends Selector<Employee> { public $Selector2(Collection<? extends Employee> c, int threshold) { super(c); this.threshold = threshold; } private final int threshold; public boolean select(Employee item) { return item.salary < threshold; } } private static class $Selector3<T> extends Selector<Employee> { public $Selector3(Collection<? extends Employee> c, int amount) { super(c); this.amount = amount; } private final int amount; public boolean select(Employee item) { return item.salary > amount; } } } class Employee { Employee(String name, boolean isManager, int salary) { this.name = name; this.isManager = isManager; this.salary = salary; } final String name; final boolean isManager; final int salary; public String toString() { return name; } } //============================================================================== // Probable candidates for java.util abstract class Selector<T> { public Selector(Collection<? extends T> c) { this.c = c; } private final Collection<? extends T> c; public abstract boolean select(T item); public Collection<T> selectAll() { return Selections.selectAll(c, this); } public Collection<T> selectAll(Collection<T> result) { return Selections.selectAll(c, this, result); } } class Selections { private Selections() {} public static <T> Collection<T> selectAll(Collection<? extends T> c, Selector<T> selector) { Collection<T> result = new LinkedList<T>(); return selectAll(c, selector, result); } public static <T> Collection<T> selectAll(Collection<? extends T> c, Selector<T> selector, Collection<T> result) { for (T obj : c) { if (selector.select(obj)) { result.add(obj); } } return result; } } |
Callable
Callable
is not in the java.lang
package. It should have been, of course. But that little mistake shouldn’t ruin Callable
’s chances of participating in this proposal on equal footing with Runnable
.