Online Training Index
101, Part III:
Writing a Simple JavaBean
by Beth Stearns
January 2001
Introduction |
Page 1 |
Page 2 |
Page 3 |
Page 4
Events, Reflection, and Introspection
This section walks you through four versions of a modest applet containing two AWT components. A ChoiceApplet class is adapted from old style AWT events to the delegation event model. You should obtain a good sense of the way Beans use reflection, introspection, event handling, and inner classes. You'll also see interesting uses of class objects and method objects combined with reflection to perform indirect method invocation.
At first, hooking event sources to event listeners that call event handlers seems a little complicated. This section clarifies what goes on behind the scenes in builder tools like BeanBox when you connect event sources to event targets. It shows you how reflection works programmatically. That is, you learn how to write programs that use introspection and reflection and which do not require builder tools to run and test.
The ChoiceApplets, versions 1-4, introduce how Beans make use of reflection, introspection, event handling, and inner classes. The applets also demonstrate differences in event handling between JDK
1.0 and JDK 1.1.
Comparing Event Models
This section compares the new and old style event models, and introduces ChoiceApplet01, the foundation for changes you will make in the four following sections. The ChoiceApplet starts with a simple Choice AWT component and adds a
TextField.
ChoiceApplet01 is built on the JDK 1.0 event model and provides a good example of how to upgrade AWT programs to take advantage of the delegation event model introduced in JDK 1.1.
Handling Events with ActionListeners.
This section illustrates a mixture of JDK 1.0 and JDK 1.1 software event handling, and it introduces the delegation event model. You'll see how TextField and Choice components can be requested to notify interested listeners about events. You will also see how to define an ActionListener inner class to handle events generated by these components. As an exercise, you can adapt the existing version of ChoiceApplet01 into a new version called ChoiceApplet02.
Upgrading Event Handlers.
Using the ActionListener event handler for the TextField as a model, you can now convert event handling code for the Choice component. This section explains the general procedure for converting code. Plus, it provides a concrete example for converting older programs to use JDK 1.1 AWT events. As an exercise, you can adapt the existing version of ChoiceApplet02 into a new version called ChoiceApplet03.
Invoking Reflected Methods
The Beans introspection API is built on top of reflection mechanisms introduced with JDK 1.1 This section shows how to invoke a method whose name has been typed into a TextField component. Though the example is artificial, it does give a sense of how to retrieve and invoke methods from Class objects based on method signatures. As an exercise, you can adapt the existing version of ChoiceApplet03 into a new version called ChoiceApplet04.
Program Source Code
You will find references to all four versions of the Choice applet covered in the next four sections. You will also find HTML code that can be used to run the applet. Or, you can use the JDK appletviewer.
Each of the subsequent sections will provide links to relevant example programs.
A makefile for this lesson automates source code compilation.
ChoiceApplet Example Programs
ChoiceApplet01.java
ChoiceApplet02.java
ChoiceApplet03.java
ChoiceApplet04.java
Comparing Event Models
This section shows you some of the advantages of the new APIs introduced with JDK 1.1. In particular, you'll see the new delegation event model in action, and see how reflection can be used to get Method objects for your Beans programmatically.
Retrieving a Method object through introspection allows you to call the method without knowing its exact definition at the time you write your code. Application builder tools, like the BeanBox, use introspection to present users with a list of event handler methods that a given Bean can respond to.
ChoiceApplet01 is JDK 1.0-compatible to illustrate the old event handling mechanism. The program starts out as a standard applet containing only an AWT Choice component.
public class ChoiceApplet01 extends Applet
{
Choice theChoice = new Choice();
public void init()
{
theChoice.addItem("Choice item # 1");
theChoice.addItem("Choice item # 2");
theChoice.addItem("Choice item # 3");
add(theChoice);
}
...
} |
The handleEvent method prints mouse movement and focus events as they occur in the applet's frame:
public boolean handleEvent(Event e)
{
System.out.println(e.toString());
return super.handleEvent(e);
}
An action method handles selections from the Choice component:
public boolean action(Event event, Object arg)
{
System.out.println("ENTER----->action");
if (event.target == theChoice)
System.out.println(
theChoice.getSelectedItem() + " selected");
System.out.println("EXIT------>action");
return true;
} |
This simple example can be run either as an applet or an application. You can use an HTML file to test these applets, or, because the ChoiceApplet01 has a main method, you can use the Java interpreter to invoke the program directly:
java ChoiceApplet01
Reflection and Method Retrieval
With the new Core Reflection API, you can use the getName construct to get the class for any object in the system:
type.getClass().getName()
For example, if "s" is a String object, then s.class returns the string "String." With the reflection API, you can:
- Obtain an object's class from the object itself, and you can then get its instance variables, methods, and ancestors.
- Use the reflection API to test for assignment compatibility.
Assignment compatibility answers the question: Can objects of type A be assigned to a variable declared as type B?
Once you have the string name for a class, you can also invoke methods on the class. For example, suppose you have a class with two methods, drawTriangle and drawRectangle:
public class DrawSomething extends java.applet.Applet {
...
public void drawTriangle() {
System.out.println("drawing a triangle");
...
repaint();
}
public void drawRectangle() {
System.out.println("drawing a rectangle");
...
repaint();
}
} |
Using reflection, you can invoke either method using a string naming the method:
public class DrawSomething extends java.applet.Applet {
...
public void invokeMethod(String mName) {
Method m =
Reflect.class.getDeclaredMethod(mName, null);
m.invoke(this, null);
}
...
} |
The next sections show you how to put reflection to good use.
Program Source Code
You may want to look at the source file for ChoiceApplet01.java.
Handling Events with ActionListeners
This section shows how it is possible to mix JDK 1.0 and JDK 1.1 event handling in the same program. However, while the two models can work together to a limited degree, mixing the event models in actual programs is highly discouraged.
- Start by adding a
TextField to ChoiceApplet02:
TextField theTextField = new TextField(20);
- Add the
TextField and an ActionListener to the applet:
add(theTextField);
theTextField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println(
"ENTER----->ActionListener");
String mName = e.getActionCommand();
System.out.println("mName: " + mName);
System.out.println(
"EXIT------>ActionListener");
}
});
|
Note that the call to addActionListener uses an inner class definition, which is created inside a single argument passed to addActionListener. Inner classes provide a powerful mechanism for simplifying and consolidating code for event registration and event handling in a single location. Without the inner class mechanism, you must write a separate ActionListener subclass to handle the actionPerformed events generated by theTextField. These actionPerformed events are generated whenever the return key is pressed with input focus on theTextField.
In this example, the actionPerformed event handler merely prints out the name of the fired event. You'll get a chance to add more useful information and actions in the next section.
- To use
ActionListenersin your code, add the followingimport statement at the top of the source file:
import java.awt.event.*;
- Use
TestChoiceApplet02.html to test the applet.
Program Source Code
You may want to look at the source file for ChoiceApplet02.java to verify the changes.
Upgrading Event Handlers
This section illustrates how to convert older style JDK 1.0 event handlers to the current JDK 1.1 style event handlers. You begin by adding methods to ChoiceApplet0 that can be called either by typing their names in the
TextField
or by selecting their names from the
Choice
component.
- Change the names of the
Strings that appear in the
Choice component:
public void init()
{
theChoice.add("big");
theChoice.add("small");
theChoice.add("unknown");
add(theChoice);
...
} |
- Change the event handling for
Choice. Use an ActionListener similar to the one used by the TextField:
theChoice.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
System.out.println(
"ENTER----->theChoice ActionListener");
String mName = (String) e.getItem();
System.out.println("mName: " + mName);
String pString =
e.paramString(); // useful for Debugging
System.out.println("pString: " + pString);
theTextField.setText(mName);
System.out.println(
"EXIT------>theChoice ActionListener");
}
}); |
The event handler prints only diagnostic information right now. Eventually you'll use the String returned by getItem to do a method lookup. Notice that theTextField is set to the string just selected in the Choice component:
theTextField.setText(mName);
With these changes you have upgraded the event handling mechanism for Choice to the JDK 1.1 style.
- Remove the old
action and handleEvent methods:
public boolean action(Event event, Object arg)
{
System.out.println("ENTER----->action");
if (event.target == theChoice)
System.out.println(
theChoice.getSelectedItem() + " selected");
System.out.println("EXIT------>action");
return true;
}
public boolean handleEvent(Event e)
{
System.out.println(e.toString());
return super.handleEvent(e);
} |
- While not mandatory, you can also change the following deprecated calls in
main.
Change:
theFrame.resize(500,300);
theFrame.show();
to:
theFrame.setSize(500,300);
theFrame.setVisible(true);
These kinds of changes are covered in: How to Convert Programs to the 1.1 AWT API. In fact, we used the updateAWT script mentioned in that document to upgrade the ChoiceApplet to use JDK 1.1 events.
Continuing the upgrade, modify the appletFrame class to implement the WindowListener interface.
class appletFrame extends Frame
implements WindowListener {
appletFrame() {
appletFrame() {
addWindowListener(this);
}
...
} |
Once appletFrame implements WindowListener, you need to provide implementions for each method declared in the WindowListener interface:
public void windowClosed(WindowEvent event) {}
public void windowDeiconified(WindowEvent event) {}
public void windowIconified(WindowEvent event) {}
public void windowActivated(WindowEvent event) {}
public void windowDeactivated(WindowEvent event) {}
You are left with only one handler with any functionality
windowClosing
:
public void windowClosing(WindowEvent event) {
System.out.println("ENTER-----appletFrame.windowClosing");
this.setVisible(false);
dispose();
System.out.println("EXIT-----appletFrame.windowClosing");
System.exit(0);
} |
The windowClosing handler now does what the old handleEvent method did within appletFrame. However, note that the updateAWT script changed another deprecated method call. The line:
this.hide();
was changed to:
this.setVisible(false);
You can use
TestChoiceApplet03.html
to test the applet.
Program Source Code
You may want to look at the final source file for ChoiceApplet03.java to verify any changes.
Invoking Reflected Methods
This section shows you how to invoke a method using reflection. In this part of the tutorial, you invoke a method whose name has been typed into a TextField. While few programs really require reflection, application builder tools rely heavily on reflection and introspection.
Start by adding two methods that you invoke using reflection. By using reflection, you can place a method name in a string and use that string to retrieve a Method object. The Method object contains the proper arguments for the specified method, and you can use this object to invoke the method.
Step 1: Import the reflection package.
To support the reflection classes, add the following package
import statement at the end of the current list of imports:
import java.lang.reflect.*;
Let's examine the methods that called based on TextField input events or Choice ItemSelected events.
Step 2: Add a paint method to draw a rectangle.
First, add a paint method to draw a rectangle of variable size:
public void paint(Graphics g) {
g.drawRect(x,y,w,h);
}
Step 3: Declare instance variables to control the rectangle's size.
Declare instance variables at the top of the class definition to control the rectangle's size:
public class ChoiceApplet04 extends Applet
{
...
int x,y,w,h;
...
}
Step 4: Define two methods to resize the rectangle.
Whenever a repaint method is called, the paint method is called automatically to update the applet's frame with the newly drawn rectangle. This is done inside two new methods that you must now define:
public void big() {
System.out.println("big()");
x=10; y=50; w=100; h=100;
repaint();
}
public void small() {
System.out.println("small()");
x=10; y=50; w=20; h=20;
repaint();
} |
Step 5: Write the invoke method.
The big method draws a big rectangle, while the small method draws a small one. You will call these methods indirectly through the Reflect lookup mechanism. The invoke method does the method lookup and subsequent method invocation:
public void invoke(String mName) {
try {
Method m =
ChoiceApplet04.class.getDeclaredMethod(
mName, null);
m.invoke(this, null);
}
catch (Exception ex) {
System.err.println(
"no such method: "+mName);
}
} |
The mName String variable contains the name of the method. Using mName, the Class object for ChoiceApplet04 calls getDeclaredMethod
to get a Method object, m, for the named method. The getDeclaredMethod takes two arguments: a String containing the method name and an array of arguments for that named method. Since no arguments are required by either the big or small methods, the second argument in our case is null:
ChoiceApplet04.class.getDeclaredMethod(mName, null);
Step 6: Invoke the invoke method.
Once you have the desired Method object in the variable m, you can invoke the method as follows:
m.invoke(this, null);
A method lookup or invocation failure throws an exception in the try block. If this happens, the exception handling code in the catch block reports that "no such method:" exists and prints the method name. In our example, the method names big or small work; all other names throw an exception.
For clarity, it's best to keep the text displayed in the TextField consistent with the selection in the Choice component and vice versa. Add the following code to the
actionPerformed method of the inner class handling events for theTextField:
if (mName.equals("big") ||
mName.equals("small"))
theChoice.select(mName);
else
theChoice.select("unknown");
Immediately following this code, add a statement to call invoke. The method uses the String value currently in theTextField:
invoke(mName);
Step 7: Add the invoke call to the event handler.
You must add the same call to the event handler code for the Choice component. The respective listener registration and event handler definitions look like this:
theChoice.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
System.out.println(
"ENTER----->theChoice ActionListener");
String mName = (String) e.getItem();
System.out.println("mName: " + mName);
String pString =
e.paramString(); // useful for Debugging
System.out.println("pString: " + pString);
theTextField.setText(mName);
invoke(mName);
System.out.println(
"EXIT------>theChoice ActionListener");
}
});
theTextField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println(
"ENTER----->theTextField ActionListener");
String mName = e.getActionCommand();
System.out.println("mName: " + mName);
if (mName.equals("big") ||
mName.equals("small"))
theChoice.select(mName);
else
theChoice.select("unknown");
invoke(mName);
System.out.println(
"EXIT------>theTextField ActionListener");
}
});
} |
Once you get comfortable with using inner classes, events, and event listeners, you'll be on solid ground for programming JDK 1.1 applications and JavaBeans. Reflection is valuable for certain situations, such as when building programming environments and tools that need to find out the capabilities of classes at run time. For typical programs, it's best to focus on the new event model.
Program Source Code
A makefile for this lesson automates source code compilation, JAR file construction, and copying of JAR files to the appropriate BeanBox directory. You may have to edit several of the variables in the makefile to indicate the location of your JDK 1.1 and BDK installation directories.
You may want to look at the source file for ChoiceApplet04.java to verify changes.

Introduction |
Page 1 |
Page 2 |
Page 3 |
Page 4
|