Welcome!

Java Authors: Jeremy Geelan, Liz McMillan, Yakov Fain, Hari Gottipati, Tad Anderson

Related Topics: Java

Java: Article

Delegates Reloaded: Walking the Path

Using the reflection API

The function pointer, a powerful concept in the C and C++ programming languages, has no direct equivalent in Java. No syntax exists to pass the address of a method to a JButton, for instance, that links it with pressing the button. Instead, Java promotes the use of anonymous inner classes, like this one:


okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
startProcessing();
}
});

Back in October 1996, in an attempt to eliminate the need for this bloated syntax, Microsoft introduced an object-oriented method pointer into J++ called a "delegate." Sun Microsystems, citing the delegate as language pollution, sued Microsoft a year later for violating its Java license agreement. The lawsuit successfully maintained the purity of Java, but it also encouraged Microsoft to develop a competing Java-like language, C#. Today, the delegate lives on in C# and the other .NET programming languages that comprise Microsoft's Common Language Runtime.

Had the delegate become part of Java, would Swing programming be easier? More than simply hooking a component to an action, the delegate could invoke methods directly or asynchronously on a worker thread. Could that technique have solved many of the Swing threading headaches that we're faced with today? This article explores these possibilities by using Java's reflection API to restore the delegate concept to Java.

Building a Delegate
java.lang.reflect.Method provides a description of a method including formal parameter types, return type, access modifier, declared exceptions, and annotations. It also provides an invoke() method to perform an indirect call; however, unlike a function pointer, Method is not bound to a particular object instance. It's part of a toolkit that includes Constructor and Field for inspecting classes. To use invoke() you must pass in a reference to an object instance.

Listing 1 demonstrates how to use it to call System.out.println() indirectly. Since the static member variable out is an instance of PrintStream, Line 4 calls getMethod() on the PrintStream Class passing in a description of the method that we're interested in. Note, many of the reflection API methods were retrofitted with varargs as of Java 5; previously, getMethod() accepted the method name followed by the method parameter types as a Class[]. Line 8 passes the string to print along with out to invoke() on the Method acquired in line 4.

To build a Delegate class, I joined together a Method and an object instance. Compare Listing 1 to the following snippet:


Delegate delegate = new Delegate(
System.out, "println");
delegate.invoke("Hello World!");

First, notice the lack of exception handling. Internally, Delegate transforms checked exceptions into unchecked exceptions for cleaner code. Second, observe that when Delegate is constructed, the println() method is not fully described; the parameter types are absent. Delegate.invoke() completes the method resolution on the first call using getClass() on each argument. Subsequent calls reuse a cached Method; however, this technique, although convenient, fails when a null parameter is used on the initial call. An alternative constructor lets you specify the argument class types explicitly like this:


Delegate delegate = new Delegate(
System.out, "println", String.class);
delegate.invoke("Hello World!");

Delegate seeks out and binds to methods of any access level, including private ones. Instead of using Class.getMethod(), which obtains only public methods, Delegate calls Class.getDeclaredMethod(). Unfortunately, both of those functions exclude inherited methods. To get around that, Delegate iteratively applies getDeclaredMethod() to all the classes in the hierarchy with the aid of Class.getSuperclass(). After locating the method of interest, Method.setAccessible(true) is used to suppress invocation access checking.

If you're surprised that you can call a private method from outside of an object, recognize that access modifiers were designed for code organization, not for security; they enable you to expose usage method while hiding implementation details. Suppressing access checking is highly beneficial for unit testing. Normally to unit test implementation methods, you have to grant them at least default (package) access; however, Delegate provides an alternative that lets you keep those methods private.

Since static methods are bound to classes, not instances, Delegate provides another constructor, which accepts a Class. The following example creates a Delegate to the Math.cbrt() function, a static method introduced in Java 5 to compute cube roots.


Delegate delegate = new Delegate(Math.class, "cbrt");
double x = (Double)delegate.invoke(8.0);

Delegate.invoke() returns type Object; in this case, the result is first cast to Double and then auto-unboxed to a double primitive.

Bridging Tiers
Delegate's primary purpose is to serve as a bridge between an event-driven front-end and a multithreaded middle tier. In Swing, when an event is triggered, it does not execute right away. Instead, it joins the event queue (java.awt.EventQueue) where it waits along with other events to be executed by the Event-Dispatch Thread (EDT). The EDT pulls events off the queue and services them one-by-one.

An event that requires a significant time to complete - either because it's computationally intensive or because it makes blocking calls - holds up the rest of the queue, causing the user interface to seem sluggish. In fact, since repaint requests traverse the queue as well, expensive events typically cause components to appear as lifeless solid-colored rectangles.

If an event can't be processed in a timely fashion, it should forward the request to a worker thread. That's the reason why Delegate provides invokeAsync(). invokeAsync() is called just like invoke(), but it dispatches a java.util.concurrent.ThreadPoolExecutor thread to execute the method. Since it's an asynchronous call, it returns immediately without a return value. To see this in use, here's an indirect call to println() on a different thread:


Delegate delegate = new Delegate(
System.out, "println");
delegate.invokeAsync("Hello World!");

Now, Swing components aren't thread-safe; they were designed to be accessed exclusively by the EDT. To safely call Swing methods from worker threads, Delegate provides invokeUI(). If invokeUI() is called by the EDT, the method is invoked directly. Otherwise, a request to execute the method is placed in the event queue. Since the call is potentially asynchronous, invokeUI() doesn't return a value. On the other hand, Delegate.invokeUIAndWait() enqueues the request, blocks until the EDT fulfills it, and then returns the result. The following example demonstrates how to obtain a JTextField value safely from any thread:


Delegate delegate = new Delegate(
textField, "getText");
String text = (String)delegate
.invokeUIAndWait();

A Listener Delegate
To join Swing events with methods, I combined delegates with a dynamic proxy. The dynamic proxy is an often-overlooked concept that's been available since Java 1.3. Before I discuss it, let's quickly review the proxy pattern.

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 (0)

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.