Event-Driven Programming: Callback Methods

Java uses event-driven programming to handle user input with GUI. When a GUI program is started there is an initialization phase, the first graphics are displayed, and then the program idles, waiting for the user to take some kind of action. By merely idling, the CPU is left free to handle other tasks until the user is ready to provide input.

Java GUI input consists of three types of objects: event generators, event listeners (called "handlers" in some languages) and event objects themselves. Event generators are any objects the user can interact with, such as buttons, text boxes, menus, etc. Listeners are registered with generators. A listener may be registered with more than one generator, and a generator may be associated with multiple listeners. Some generators create multiple types of events. For example, the mouse can generate "mouseUp", "mouseDown", and "mouseClicked" events. There is a separate callback method defined for each of the event types for each type of generator. When an event occurs at a generator, an event object is created that incorporates all the pertinent information about the event. This event object is passed as a parameter to the generator's listeners when invoking the appropriate callback method on them.

As an example, consider this simple program, FirstTest_withOutput. It displays a simple frame on the screen. In addition, it generates a steady stream of output to the console window, one line per second. When the user clicks on the "close window button" (in the upper right-hand corner on Windows' machines), the frame goes away. However, the program itself continues to run--output continues to appear in the console window. This happens because Java actually uses two "threads" to run GUI programs. One thread handles the GUI aspects of the program, while the other actually executes the main() method and those methods that it invokes. Clicking on the close button causes the GUI thread to terminate, but not the "main" thread. This is the default behavior that is inherited by our FirstFrame2 class from its parent class, JFrame. To get a different behavior, we have to provide our own callback method.

A JFrame is an event generator. Java defines an interface, WindowListener, each of whose seven methods corresponds to one of the event types that can be generated by a JFrame (WindowActivated, WindowClosing, WindowClosed,WindowDeactivated, WindowIconified, WindowDeiconified, and WindowOpened). We want to provide our own WindowClosing method so that when it is invoked by the JVM it will cause the entire program to terminate. So one solution is to define our own class, MyWindowListener, that implements the WindowListener interface. Of course, to implement the interface we need to provide an implementation of each of the seven methods listed above. All of these can be simple no-ops except for the WindowClosing method, which we will define as invoking System.exit(0); We then make an instance of MyWindowListener and register it with our frame object. When the close button is clicked, the WindowClosing method of our MyWindowListener will be invoked, and the program will terminate. Yeah!

We can save ourselves some typing by using Java's WindowAdapter class. This class implements the WindowListener interface by providing a no-op implementation of each of the WindowListener's seven methods. We define MyWindowListener as a subclass of WindowAdapter, thus inheritting these seven no-op methods. Then we only have to provide an override definition of the WindowClosing method to finish things up. That solution is presented in the NotHelloWorld_noInnerClass class.

It turns out that we can save even more typing by defining the MyWindowListener class as an anonymous inner class to our Frame, we do that in the NotHelloWorld_withInnerClass class. There are other stronger reasons to want this to be an inner class, and we'll get to those reasons later.