"You know," said Arthur, "it's at times like this, when I'm trapped in a Vogon airlock with a man from Betelgeuse, and about to die of asphyxiation in deep space, that I really wish I'd listened to what my mother told me when I was young."
"Why, what did she tell you?"
"I don't know, I didn't listen."
JavaBeans can be configured to "listen" to other software objects. And as you'll see, a Java 1.1 class (including any JavaBean) can listen not only to its parent, but also to any class that produces events by becoming an event listener. The event listener idea is central to how Java classes (and other JavaBeans) handle events.
Last month we discussed a specific type of event listener: the PropertyChangeListener interface that takes action when other Beans' properties change. This month we'll take a closer look at the whole concept
of an "event listener." You'll see how event listeners are used in the new AWT. (When I say "new," I mean since JDK 1.1 was
released.) We'll talk about how to define your own event types, and then make those new event types visible to other classes.
Then, as an example, we'll extend the BarChartBean, creating a new event type and then using it to wire the BarChartBean to
another Bean. We'll go over some details about how to write event listener interfaces, using the AWT as an example, and conclude
with a discussion of inner classes, a new Java 1.1 language feature.
I'd also like to introduce two new icons that will help identify key points:
| The JavaBean icon is a key concept for JavaBeans. |
| And the cuppajoe icon indicates new or key ideas specific to the Java language. |
A software event is a piece of data that indicates that something has occurred. Maybe a user moved the mouse. Maybe a datagram just arrived from the network. Maybe a sensor has detected that someone's just come in the back door. All of these occurrences can be modeled as events, and information about what happened can be included in the event. Often it's convenient to write software systems in terms of event processing: Programming then becomes a process of specifying "when this happens, do that." If the mouse moved, move the cursor with it; if a datagram arrived, read it; if there's an intruder, play the recording of Roseanne releasing the rabid Rottweiler.
Usually, an event contains information about the event source (the object that created or first received the event), when the event occurred, and some subclass-specific information that the event receiver can use to figure out what happened and what to do. In a windowing system, for example, there might be a subtype of event for mouse clicks. The mouse click event would include the time the click occurred, and also might include such information as where on the screen the mouse was clicked, the state of the SHIFT and ALT buttons, and an indication of which mouse button was clicked. The code that handles the event is called, strangely enough, the event handler.
So, what does this have to do with JavaBeans? Events are the primary way that Beans communicate with each other. As we'll see below, if you choose events and their connections judiciously, you can interconnect Beans in your application, tell each Bean what to do in response to the events it cares about, and the application simply behaves. Each Bean minds its own business, responding appropriately to incoming events, and sending new events to interested neighboring Beans as new situations occur. Once you understand how to use events, you can write Beans that use them to communicate with other components. And what's more, external systems like Integrated Development Environments (IDEs) automatically detect the events your Beans use and let you interconnect Beans graphically. IDEs also can send events to and receive events from JavaBeans, essentially controlling them from the outside.
In order to understand how events work with Beans, though, you've got to understand how they work in Java. And events work differently now that JDK 1.1 is the standard.
In the Java Developer's Kit (JDK) 1.0, events were used primarily in the Abstract Windowing Toolkit (AWT) to tell classes when something happened in the user interface. Programmers used inheritance to process events by subclassing an object that could receive that event type and overriding how the parent class processed the event.
For example, in Java version 1.0, the only way to get an Action event was to inherit from some other class that already knew how to handle Action events:
public class MyButton extends java.awt.Button
{
// Override action() method to handle action event
public boolean action(Event evt, Object what) {
// Now do something with the action event
}
}
This means that only classes that inherited from java.awt.Button could do anything to respond to a button click. This structure left Java events tied to the user interface and inflexible. It wasn't easy to create new event types. And even if you could create new events, it was hard to change the events that a class could respond to, because that information was hard-coded into the "family tree" (the inheritance graph) of the AWT.
The new JDK 1.1 has a more general event structure, which allows classes that produce events to interact with other classes that don't. Instead of defining event-processing methods that client subclasses must override, the new model defines interfaces that any class may implement if it wants to receive a particular message type. (You may hear this referred to as processing events by delegation instead of by inheritance.) Let's continue with this JDK 1.0 button example and move toward the JDK 1.1 model.
Let's say I wanted to create a new class that does something when a button is pressed. In 1.0, I'd have to subclass java.awt.Button to handle the Button action event, and then that button would somehow notify my new class that the button press had occurred:
//... elsewhere in the program we define the object that "listens"
// for button actions.
ActionListener myActionListener = new ActionListener();
//...
// Button action event
public class MyButton extends java.awt.Button
{
// Override action() to notify my new class
public boolean action(Event evt, Object object)
{ myActionListener.action(evt, object);
}
}
Now the object myActionListener receives an event every time any MyButton is pushed. myActionListener is not necessarily a subclass of java.awt.Component, but it does contain an action() method. We call the new class an ActionListener because, seen from the new class's point of view, it is "listening" for action events on the button it is "attached" to.
There are still some problems, though: