Welcome!

Java Authors: Maureen O'Gara, Liz McMillan, Walter H. Pinson, III, Yakov Werde, Tony Bishop

Related Topics: Java

Java: Article

Restoring the "Delegate" Concept To Java

Had the "Delegate" Become Part of Java, Would Swing Programming Be Easier?

To demonstrate delegates in action, I put together a simple fractal image explorer. The application consists of an image window and a progress dialog. When you click on a point of the fractal, the progress dialog in Figure 3 appears, and a worker thread starts to compute a zoomed in region. The Cancel button lets the user terminate the running computation.

I opted not to use javax.swing.ProgressMonitor because it's not modal, it's difficult to get it to appear, and its cancel button causes it vanish immediately. Instead, I created a ProgressDialog class to encapsulate a dialog with a JLabel, a JprogressBar, and a JButton.

ProgressDialog monitors the progress of an object implementing IProgress, the interface in Listing 4. ProgressDialog contains a Swing timer. Every 0.25 seconds, it updates the progress bar using IProgress.getCurrent(). If the user presses Cancel, it invokes IProgress.requestCancel(), sending out a request that may not be satisfied immediately. The dialog remains visible until IProgress.isDone() return true, indicating that either the computation completed fully or it was terminated gracefully by the user.

Note: the implementation of IProgress must be thread-safe and its methods must return rapidly. For instance, in the fractal explorer, requestCancel() sets a volatile cancel-request flag instead of blocking until the worker thread terminates.

A cycle starts when a mouse click launches a worker thread using UIDelegate:


UIDelegate.create(this, "mouseClicked", "zoom", true)

Before computing the fractal, the worker thread calls the non-blocking ProgressDialog.show() method. show() safely manifests the dialog and kicks off the Swing timer by forwarding the call with Delegate.invokeUI(). The timer is linked to checkProgress(), shown in Listing 3, using:


UIDelegate.create(this, 
  "actionPerformed", "checkProgress", 
  false)

When isDone() return true, the optional ProgressDialog constructor parameter, completedDelegate, is called back on the EDT with the final progress value. If the completed progress is 100%, the image is obtained using IProgress.getResult(), and pasted to the window.

See the sidebar for details on the image-generation algorithm.

Pros and Cons
Delegates provide cleaner connections between components and handlers both syntactically and thread-wise. The progress monitor example demonstrates how you can focus more on visual design and business logic than on juggling threads. However, Delegate is not type-safe; it's easy to get a runtime exception, for example, after misspelling a function name. Adding a type-safe delegate to a future version of Java might be asking for too much, but what about a method keyword, something like this:


Method m = this.actionPerformed(
    ActionEvent.class).method;

No need to catch a NoSuchMethodException since the check occurs at compile time.

Also, we're always told to avoid reflection because of its effect on performance. It's conceivable that searching for the Method corresponding to a specified name and a set of parameters takes a performance hit, but once located, what's the penalty of the Method.invoke() call itself? To gain a rough sense the answer, I used System.nanoTime() in a simple test harness like this:


long start = System.nanoTime();
for(int i = 0; i < NUM; i++) {
  // ... direct/indirect method call
}
long end = System.nanoTime();
double average = (end - start)
    / (double)NUM;

I ran my tests using Sun's Java 5 JVM with the default settings on a 1.8GHz PC running Windows XP. On my machine, it takes about 3.5ns (3.5 _10-9 seconds) for a normal method call and about 150ns for a delegated call. Although that's around 43 times slower, it still means that you can make 6.6 million delegated calls a second. Swing applications, at the very least, should be able take full advantage of Delegates without noticeable delays.

You can checkout my performance test harness along with the other code discussed in this article online at www.sys-con.com/java/sourcec.cfm.

References

  • .NET Delegates: http://msdn.microsoft.com/library/default.asp?url=/ library/en-us/csref/html/vcrefTheDelegateType.asp
  • Reflection API Tutorial: http://java.sun.com/docs/books/tutorial/reflect/
  • Using a Swing Worker Thread: http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
  • Explore the Dynamic Proxy API: http://java.sun.com/developer/technicalArticles/DataTypes/proxy/
  • Using Dynamic Proxies to Generate Event Listeners Dynamically: http://java.sun.com/products/jfc/tsc/articles/generic-listener2/index.html
  • How to Use Progress Bars: http://java.sun.com/docs/books/tutorial/uiswing/components/progress.html
  • RMI Tutorial: http://java.sun.com/docs/books/tutorial/rmi/
  • Mandelbrot Set: http://mathworld.wolfram.com/MandelbrotSet.html

    Sidebar 1

    Dialog Patterns
    Switching from the EDT to a worker thread and back again to complete a business task maintains user interface responsiveness, but it breaks up a conceptually linear flow of execution into scattered, disjoint pieces. Consider a menu item that pops up in an options dialog before executing a task. To avoid the threading issues discussed in body of this article you can link the OK button on the dialog to the business logic with Delegate.invokeAsync(). When the task completes, it callbacks a success or failure method with Delegate.invokeUI(). That technique certainly works and often can't be avoided, but whenever possible, strive for a linear flow of execution.

    One way to achieve this is to follow the pattern used by JOptionPane.showInputDialog(). showInputDialog() displays an input dialog and it blocks until the user enters a string. A complicated options dialog, as mentioned in the previous example, would return a bean containing a set of fields. We can link the menu item that manifests the dialog to a controller method using UIDelegate and we can configure it to call the controller with a worker thread. The controller provides the linear flow: it shows the dialog, it blocks for input, it validates the input, and it either displays an invalid fields message or it runs the business logic. The show() method of such a dialog must be thread-safe, unlike showInputDialog() which is designed to be exclusively invoked by the EDT. See the show() method of ProgressDialog for an example of how to do this.

    Sidebar 2

    The Mandelbrot Algorithm
    The Mandelbrot fractal is the intersection of mathematics, computer science and art. It's amazing that something so visually attractive is generated by such a simple algorithm.

    The fractal relies on basic properties of complex numbers. A complex number is a mathematical construct analogous to an object with two fields. It's written as the sum x + yi, where x and y are real numbers (double types) and i is the imaginary constant defined by the relation i2 = -1. Each coordinate [x, y] represents a point on the complex plane. The magnitude of a complex number is the distance between that point and the origin, computable using the Pythagorean Theorem.

    The image is generated by row scanning a rectangular region of the complex plane and assigning each point C = [x, y] a color by repeating the rule ZN+1 = ZN2 + C, where Z0 = C until the magnitude of ZN+1 exceeds 2. The value of N when the loop terminates determines the color of point C. If the loop fails to terminate after a larger number of iterations, then C is part of the Mandelbrot set and that point is traditionally assigned the color black. For those whose algebra is rusty, given C = [x, y] and ZN = [a, b], ZN+1 = ZN2 + C = (a + bi)2 + (x + yi) = a2 + 2abi + (bi)2 + x + yi = a2 + 2abi - b2 + x + yi = [a2 - b2 + x, 2ab + y].

    For further fractal exploration, see the references.

  • More Stories By Michael Birken

    Michael Birken is actively involved in the design and research of emerging trading technologies at a Manhattan-based financial software company. He's a Sun Certified Java programmer and developer. Michael holds a BS in computer engineering from Columbia University.

    Comments (9) View Comments

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


    Most Recent Comments
    Black007_pl 05/20/08 10:28:41 AM EDT

    Hi!
    I don't really see the point of using delegates in java. The code with ActionListeners is more clear to me than using some function pointer, and passing them into UI elements. It implements observer/obesrvable pattern, and it's more objective for me than delegates... You can easily implement your won calls that will implement this pattern just by having a list of objects that implemet some interface and invoking it's method, nothing more....
    And when I saw how my collegue tries to implemet his won delegates in C# it was very difficult to me to understand what his trying to do (he also didn't know it... :D )

    Have a nice day

    Tom 04/13/05 09:10:30 AM EDT

    Oh, another suggestion is to use a more Microsoft alike approach. For example when instead of:

    container.add(button)

    the following is writen:

    container.add(SwingEventDispatcher.wrap(button, "store", this));

    This line still adds the button to the container, but also names the button "store" and binds it to "this" by searching for methods with a particular pattern, for example:

    public void store_actionPerformed(ActionEvent e)

    Note the "store_" prefix. To make the source more readable this method may also be written as:

    public void store_clicked(ActionEvent e)

    And some intelligence is added also:

    public void store_singleClick(ActionEvent e)
    public void store_doubleClick(ActionEvent e)
    public void store_tripleClick(ActionEvent e)
    public void store_multiClick(ActionEvent e)

    It makes sure that on a double click only xxx_doubleClick is called and not xxx_singleClick also.

    The same trick works with all events:

    mybean_propertyChanged(PropertyChangeEvent e)
    mylist_selectionChanged(...)
    ...

    Tom 04/13/05 08:46:16 AM EDT

    A cleaner solution (IMHO) are closures. Using closures would result in something like:

    JButton button = new JButton();
    button.addActionListener() { e | button.setText("bla"); }

    Closures basically are inner classes that can access its context without having to use final (the local variable button is used in the inner class). Groovy provides a (hopefully soon) standard way to do this, but at least Groovy is fully Java compatible.

    A Wolfe 04/12/05 11:21:22 AM EDT

    Maybe I'm not following the argument here, but I guess I wonder why someone would prefer to pass around function pointers rather than interfaces. Can't the dispatch code just as easily invoke a method through an interface as through a Delegate class?

    I guess I am uncomfortable with the modularity here. First of all, actual implementation classes have a sort of rigidity from the standpoint of the client and the programmer. So I like having interfaces anyway. But the other issue is that a method may start out being 'standalone', but then it may need to be called as part of a sequence of other methods. If you delegate through Interfaces, then it would seem you could add those other methods very easily.

    Finally, variable argument lists are prone to incorrect use in all circumstances; but in this one there is no commonalties among the arguments. The classic 'printf' variety of variable argument lists was very clear -- each of the variable arguments is intended to be rendered as text. In this case it can be results of embedded function calls or anything. I guess I don't understand why one would want to suppress type checking.

    Anyway I have really not gotten the idea or the benefit of this technique. Sometimes it's worthwhile to emulate one language's constructs in another language, but I don't see it here.

    Ross Judson 04/12/05 09:08:06 AM EDT

    There's nothing wrong with inner classes per se; they represent a superset of the functionality of delegates or method pointers. The problem is the Java language itself, and the lack of lightweight anonymous function/class declaration, and the lack the type inferencing. Fix those two things and the ugly part of inner classes (the declaration syntax) goes away.

    Scala does lightweight functional syntax very nicely. If func takes something like an action listener, you can call it like this (for arrow read greater-equals):

    func(event "arrow" event.getComponent().setSelected(true));

    Scala knows the expected type for the call and builds the scaffolding necessary. Under the covers you end up with:

    func(new ActionListener() {
    public void actionPerformed(Event event) {
    event.getComponent().setSelected(true)
    }});

    Michael Abernethy 04/11/05 01:01:16 PM EDT

    The Buoy package contains some things similar to what you've outlined in your article. Although I have not worked with, reading the examples makes it appear that your goals of making the event handling code easier exists already in Buoy.

    Kevin Timm 04/11/05 10:16:11 AM EDT

    One of the things I miss the most about C is the lack of pointers to functions. Used correctly, these are fantastically useful. Hopefully, some of that can be added back (or implemented as is shown here) for the greater good of all.

    Remember too, the 'Evil Empire' did not invent this desire (as noted by a previous respondent) they only implemented a shortcoming that they saw. This is not a bad thing.

    Javanaut 04/11/05 07:15:38 AM EDT

    ### Dennis Farr commented ...
    Great article. I have been haunted by the thought that not all differences between C# and Java were abberations, and your article shows how to take advantage of the good ideas coming out of the evil empire and use them inside Java. ###

    The idea is certain to provoke widepsread discussion - it reminds how close C# and Java are.

    Dennis Farr 04/10/05 01:23:31 PM EDT

    Great article. I have been haunted by the thought that not all differences between C# and Java were abberations, and your article shows how to take advantage of the good ideas coming out of the evil empire and use them inside Java. Java is at least open enough to discuss and use ideas from outside Sun, as I hope yours will be.