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() { block.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.
13 comments:
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).
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.
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.
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 http://www.joelonsoftware.com/items/2006/08/01.html, http://ivan.truemesh.com/archives/000637.html, http://www.talios.com/dear_java_i_need_closure.htm, and http://blog.moertel.com/articles/2005/08/30/closures-and-the-professional-programmer; 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.
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.
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;})
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:
Thanks,
Mike
http://weblogs.java.net/blog/forax/archive/2007/02/closure_littera.html
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.
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?
@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.
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.
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"); })
p.add(c);
}
Have you spotted the error?
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?
Post a Comment