Monday, March 05, 2007

Java Closures versus MouseListener

The Closures for Java proposal simplifies the code for many purposes where anonymous class instance creation expressions are currently used. When the anonymous class's supertype is an interface with a single abstract method, a closure can be used directly. But if the supertype is a class, like java.util.TimerTask, or has more than one method, like java.awt.event.MouseListener, then you can't use a closure directly. You can still use an anonymous inner class directly, as always, but there are ways of using closures that may be more convenient. For TimerTask, a client can be written this way

void printHelloAfterDelay(java.util.Timer timer, long delay) { timer.schedule(TimerTask.of({ => System.out.println("Hello"); }), delay); }

if we add the following utility method to TimerTask:

public TimerTask of(final Runnable block) { class ClosureTimerTask extends TimerTask {
public void run() {; }
return new ClosureTimerTask(); }

Similarly, Peter von der Ahé showed me how to use the builder pattern, along with closures, to simplify handling mouse events:

void addSomeActions(java.awt.Component foo) { foo.addMouseListener(new MouseListenerBuilder() .setMouseClicked({ MouseEvent e => System.out.println("Mouse clicked"); }) .setMouseReleased({ MouseEvent e => System.out.println("Mouse released"); }) .setMouseEntered({ MouseEvent e => System.out.println("Mouse entered " + e.getComponent()); })); }

The implementation of MouseListenerBuilder is left as an exercise to the reader.


axel said...

A lot can be learned about the relation between OO and functional programming from this pattern - it's essantially an emulation of OO (minus "this" and inheritance) using imperative/functional constructs.

Just see how close this is to:

void addSomeActions(java.awt.Component foo) {
foo.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) { System.out.println("Mouse clicked"); }
public void mouseReleased(MouseEvent e) { System.out.println("Mouse released"); }
public void mouseEntered(MouseEvent e) { System.out.println("Mouse entered " + e.getComponent()); }

I just had to move some parens around, remove the fat arrow, and declare visibility (public) and return type (void).

Anonymous said...

Where has my easy-to-read-Java gone to?

I need to get used to this. I have seen every presentation, every interview and everything that is on the web for this proposal, but still I dont know whether I will be using this new idioms.

Thank you for your hard work, Mr. Gafter. But I am afraid I am not overwhelmingly excited about this.

Anonymous said...

Hi Neal, have you had the chance to look at the FCM (First Class Method) proposal. I think their approach to use a method literal (like #) for this sort of things looks more straightforward in my opinion, and maybe can be adapted to the closure proposal. besides the # can also be used to reference property literals as well, so it might provide dual use.

Neal Gafter said...

Yes, I've looked at the FCM proposal. I agree that the idea of currying a method with respect to its receiver is useful (as is currying support in general). I don't have a strong opinion about the choice of syntax.

FCM wasn't intended to support control abstraction or refactoring use cases like,,, and; The authors intended FCM to be mostly about syntax sugar, providing a better
syntax for code that can be written today. I understand the authors are working on a complementary proposal to support these kinds of use cases. I look forward to the result.

mm said...

it reminds me about the Object initializer in javascript.

void addSomeActions(java.awt.Component foo) {
foo.addMouseListener(new MouseListenerBuilder(
MouseClicked: { MouseEvent e => System.out.println("Mouse clicked"); }
MouseReleased: { MouseEvent e => System.out.println("Mouse released"); }
MouseEntered: { MouseEvent e => System.out.println("Mouse entered " + e.getComponent()); }

In some case it is clear to read and easier to write, at least it don't have to introduce new classes in the existing library. On the other hand, we can learn less and write more.

Stefan Schulz said...

I am not sure, if building a full instance of an interface having more than one abstract methods is a use case for either approach, be it BGGA, CICE, or FCM. There is not much to gain using a MouseListenerBuilder compared to declaring an anonymous class, except, maybe, having no need for defining stubs. It's not getting more readable, or shorter if fully fledged (as axel has shown). Might be useful to have a MouseListenerBuilder, though, providing a method like:
onAny({MouseEvent e => block;})

Anonymous said...

My previous apparently did go through, so here it is again:

Neal, I agree with you regarding the power of the BGGA closure proposal. I personally like its control abstraction support quite a bit actually. What I was trying to say is that the FCM proposal offers some interesting simplification ideas from which the BGGA proposal can certainly benefit.
To further explain my point please take a look at the following Remi's blog post on this issue:


Jochen "blackdrag" Theodorou said...

comparing the builder version and the normal inner class version, the builder version has still 2 advantages:
1) I can use local variables without making them final, which means I can share a modifiable state across multiple classes without creating a dummy class.
2) I consider the builder version as more readable.

I think the last point is a matter of taste and training, but even thought having a more mighty construct by not really typing more seems to be a win.

Tim said...

I am programming in Swing 8 hours a day , 5 days a week, so I am using MouseListeners a lot. I doubt that Neil and Peter are using Swing very often, because otherwise you would have come up with axel's code yourself (which btw is the standard way of implementing MouseListeners).

Maybe I am not smart enough, but I just do not see what I would gain from closures in this particular case. Simply being able to say "wow, you can do this with closures, too" is not really a strong argument, is it?

Neal Gafter said...

@Tim: this is not intended to be an argument of why closures are needed in the language. It is just an example showing how you can do something. I believe the control abstraction and refactoring use cases are much more compelling as an argument for why something along these lines deserves to be added to Java.

axel said...

Refactoring is an interesting point!

If, for example, you want to extract a general "ToggleStatusLineOnMouseEnter" mouse listener and instantiate it in different places, then that's going to be a class. It's easier to go from an anonymous inner class to a top-level class than to refactor the corresponding closure solution into a class-based solution. Todays IDEs can already do the former refactoring.

The closure builder pattern may be most appropriate for the kind of top-level component wiring that has a low chance of being reused.

Making closure-based OO extensible and code-reusable would probably go into the direction of prototype-based OO, which however does not mesh well with lexical scoping for state representation.

Karsten Wagner said...

Using the final constraint has it's advantages. Let's look at an example:

JPanel p = new JPanel();
for(int i = 0; i < components.length; i++) {
JComponent c = new JButton("button " + i);
c.addMouseListener(new MouseListenerBuilder()
.setMouseClicked({ MouseEvent e => System.out.println("Button " + i + " clicked"); })

Have you spotted the error?

Michael Nischt said...

I maybe wrong, but axel's method is not only easier, but probably more performant. I mean the MouseListener created by a MouseLisenterBuilder has to call the closure (the invoke method of the generated anonymous class), where the MouseAdapter executes the code directly.

Or is this done in a better way?