This article demonstrates how to address these issues by using sophisticated event adapter classes designed to improve on those provided with JDK 1.1.
The following applet demonstrates this article's sophisticated adapters in action. The top row of the applet uses the sophisticated adapters, with the result that various errors are caught and the user is notified. The Long Asynch Action button shows the asynchronous behavior in action; notice that the GUI is not hung while it sleeps.
The bottom row uses standard adapters; note that the errors detected by the top components go undetected. The Long AWT-Blocking Action button shows the GUI hung.
Note: Not all browsers have complete JDK 1.1 support. If the applet does not appear in a pop-up window in the upper left-hand corner of your browser window, you will need to download the applet. To do so, just click on this handy zip file.
First, let's look at the simple event adapters you get with the JDK 1.1. They aren't very complicated because each method
in each JDK 1.1 adapter does absolutely nothing. There is no ActionAdapter class for the simple ActionListener interface, but if an ActionAdapter class existed, it would look like this:
public class ActionAdapter implements ActionListener
{
public void actionPerformed (ActionEvent e)
{
}
}
The other adapter classes provided by the JDK are larger, but no more complicated. These simple adapters do have good features:
Unfortunately, the JDK 1.1-provided adapters also have four major limitations.
Quick, answer this: Can this method throw any exceptions?
public void niftyMethod () // Note: no throws clause!
{
// Do stuff.
}
In fact, it can. The Java language defines an entire class hierarchy of Exception objects that can be thrown even without a throws XXXException clause. These exceptions descend from a base class named java.lang.RuntimeException. Examples include IndexOutOfBoundsException and NullPointerException.
A RuntimeException usually indicates that there is a bug in the program; it may be trying to use a null reference or indexing off the end of
an array. Nevertheless, the standard adapters allow programming errors like this to go undetected by the program and unreported
to the user of the program. The practical result is that programs can fail silently, preventing the user from discovering
the problem until it's too late.
The following sequence illustrates this scenario:
User: "Please save my work."
Java Program: Hmmm. There's a null pointer exception so I can't save
the work ... but it isn't caught, so I don't need
to tell the user.
"OK!"
User: "Exit."
Java Program: "Bye!"
User: "Hey, where's my file?!?"
Well-behaved programs should never fail silently. A better exchange would look something like this:
User: "Please save my work."
Java Program: Hmm. There's a null pointer exception so I can't save
the work. Ah, I'll report it.
"I can't save your work because I got a null pointer
exception!"
User: "What the heck is a null pointer? Well, just to be
safe, I think I'll save this to the clipboard and
dump it in a text file ..."
The user might still lose some information (formatting, in this example) but this is better than losing everything.
Next question: Can anything be thrown from the following improved function?
public void niftyMethodTwo ()
{
try
{
// Do stuff.
}
catch (Exception e) // Note: We are catching all exceptions!
{
// Panic.
}
}
Again, the question has an affirmative answer. The Java language defines an entire category of failures, called Errors, that are not exceptions. Errors usually indicate that something has gone wrong with the underlying virtual machine or with the program itself. Examples include
OutOfMemoryError and StackOverflowError.
As with runtime exceptions, errors should not happen during program execution. Again, however, if they do happen, the user
should receive notification. The following exchange illustrates the problem resulting from not dealing with Errors:
User: "Please save my work."
Java Program: Hmm. I'm out of memory so I can't save the work,
but errors aren't caught so I don't need to tell
the user.
"OK!"
User: "Exit."
Java Program: "Bye!"
User: "Did I lose my file again?!?"
Again, if users receive notification of the problem, they can at least try to recover their work:
User: "Please save my work."
Java Program: Hmm. I'm out of memory so I can't save
the work. Ah, I'll report it.
"I can't because I'm out of memory!"
User: "Darn. Close these other files."
Java Program: "OK."
User: "Now please save my work."
Java Program: Hmm, everything worked that time.
"OK!"
User: "That worked well. I'm going to start a fan
club for the programmers who wrote this program."
To reiterate, well-behaved programs should not fail silently.
All the methods in the default adapters run on the AWT thread. Both the AWT and Swing are single-threaded, so the entire GUI freezes while an action executes. Even the application's other frames will not respond to new GUI events while the application handles a prior event. Indeed, if the action runs for very long, the application can appear to be hung.