| By Michael Birken | Article Rating: |
|
| July 1, 2005 11:00 AM EDT | Reads: |
13,567 |
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.
Published July 1, 2005 Reads 13,567
Copyright © 2005 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
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.
- It's the Java vs. C++ Shootout Revisited!
- Patterns for Building High Performance Applications
- Asynchronous Logging Using Spring
- Java for Programmers (2nd Edition)
- Cross-Platform Mobile Website Development – a Tool Comparison
- Three Buzzwords That Every CIO Hears but One They Should Listen To
- Write Once Run Anywhere or Cross Platform Mobile Development Tools
- Immersing into JavaScript Frameworks
- Workday Reportedly Prepping to Go Public
- Cloud Expo New York: The Java EE 7 Platform - Developing for the Cloud
- Book Review: Sams Teach Yourself Java in 24 Hours
- OpenOffice.com Lives
- Book Excerpt: Introducing HTML5
- Adobe Sends Flex to the Apache Foundation
- Five Years Waiting for JRE 7: Is It Justified? (Part 1)
- Book Excerpt: Java Application Profiling Tips and Tricks
- i-Technology in 2012: Five Industry Predictions
- It's the Java vs. C++ Shootout Revisited!
- Patterns for Building High Performance Applications
- OpenXava 4.3: Rapid Java Web Development
- The Next Web Architecture
- Asynchronous Logging Using Spring
- Java for Programmers (2nd Edition)
- Is Write Once Run Anywhere Ever Going to Be a Reality?
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- JavaServer Faces (JSF) vs Struts
- The i-Technology Right Stuff
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- Why Do 'Cool Kids' Choose Ruby or PHP to Build Websites Instead of Java?
- What's New in Eclipse?
- i-Technology Predictions for 2007: Where's It All Headed?



















