Wednesday, December 16, 2009

Local and Anonymous Inner Classes










[Page 834 (continued)]

Local and Anonymous Inner Classes


In this next example, ConverterFrame, a local class, is used to create an ActionEvent handler for the application's two buttons (Fig. F.2).


Figure F.2. The use of a local class as an ActionListener adapter.

(This item is displayed on page 835 in the print version)





import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class ConverterFrame extends JFrame {
private Converter converter = new Converter(); // Reference to app
private JTextField inField = new JTextField(8);
private JTextField outField = new JTextField(8);
private JButton metersToInch;
private JButton kgsToLbs;

public ConverterFrame() {
metersToInch = createJButton("Meters To Inches");
kgsToLbs = createJButton("Kilos To Pounds");
getContentPane().setLayout( new FlowLayout() );
getContentPane().add(inField);
getContentPane().add(outField);
getContentPane().add(metersToInch);
getContentPane().add(kgsToLbs);
} // ConverterFram()

private JButton createJButton(String s) { // A method to create a JButton
JButton jbutton = new JButton(s);
class ButtonListener implements ActionListener { // Local class

public void actionPerformed(ActionEvent e) {
double inValue = double.valueOf(inField.getText()).doubleValue();
JButton button = (JButton) e.getSource();
if (button.getText().equals("Meters To Inches"))
outField.setText(""+ converter.new
Distance().metersToInches(inValue));
else
outField.setText(""+ converter.new Weight().kgsToPounds(inValue));
} // actionPerformed()
} // ButtonListener class
ActionListener listener = new ButtonListener(); // Create a listener
jbutton.addActionListener(listener); // Register buttons with listener
return jbutton;
} // createJButton()

public static void main(String args[]) {
ConverterFrame frame = new ConverterFrame();
frame.setSize(200,200);
frame.setVisible(true);
} // main()
} // ConverterFrame class




As we have seen, Java's event-handling model uses predefined interfaces, such as the ActionListener interface, to handle events. When a separate class is defined to implement an interface, it is sometimes called an adapter class. Rather than defining adapter classes as top-level classes, it is often more convenient to define them as local or anonymous classes.


The key feature of the ConverterFrame program is the createJButton() method. This method is used instead of the JButton() constructor to create buttons and to create action listeners for the buttons. It takes a single String parameter for the button's label. It begins by instantiating a new JButton, a reference to which is passed back as the method's return value. After creating an instance button, a local inner class named ButtonListener is defined.


The local class merely implements the ActionListener interface by defining the actionPerformed method. Note how actionPerformed() uses the containing class's converter variable to acquire access to the metersToInches() and kgsToPounds() methods, which are inner class methods of the Converter class (Fig. F.1). A local class can use instance variables, such as converter, that are defined in its containing class.


After defining the local inner class, the createJButton() method creates an instance of the class (listener) and registers it as the button's action listener. When a separate object is created to serve as listener in this way, it is called an adapter. It implements a listener interface and thereby serves as adapter between the event and the object that generated the event. Any action events that occur on any buttons created with this method will be handled by this adapter. In other words, for any buttons created by the createJButton() method, a listener object is created and assigned as the button's event listener. The use of local classes makes the code for doing this much more compact and efficient.


[Page 835]

Local classes have some important restrictions. Although an instance of a local class can use fields and methods defined within the class or inherited from its superclasses, it cannot use local variables and parameters defined within its scope unless these are declared final. The reason for this restriction is that final variables receive special handling by the Java compiler. Because the compiler knows that the variable's value won't change, it can replace uses of the variable with their values at compile time.





[Page 836]

Anonymous Inner Classes


An anonymous inner class is just a local class without a name. Instead of using two separate statements to define and instantiate the local class, Java provides syntax that lets you do it in one expression. The following code illustrates how this is done:


private JButton createJButton(String s) {        // A method to create a JButton
JButton jbutton = new JButton(s);

jbutton.addActionListener( new ActionListener() { // Anonymous class
public void actionPerformed(ActionEvent e) {
double inValue = double.valueOf(inField.getText()).doubleValue();
JButton button = (JButton) e.getSource();
if (button.getLabel().equals("Meters To Inches"))
outField.setText("" + converter.new
Distance().metersToInches(inValue));
else
outField.setText("" + converter.new Weight().kgsToPounds(inValue));
} // actionPerformed()
}); // ActionListener class
return jbutton;
} // createJButton()


Note that the body of the class definition is put right after the new operator. The result is that we still create an instance of the adapter object, but we define it on the fly. If the name following new is a class name, Java will define the anonymous class as a subclass of the named class. If the name following new is an interface, the anonymous class will implement the interface. In this example, the anonymous class is an implementation of the ActionListener interface.


Local and anonymous classes provide an elegant and convenient way to implement adapter classes that are intended to be used once and have relatively short and simple implementations. The choice of local versus anonymous should largely depend on whether you need more than one instance of the class. If so, or if it is important that the class have a name for some other reason (readability), then you should use a local class. Otherwise, use an anonymous class. As in all design decisions of this nature, you should use whichever approach or style makes your code more readable and more understandable.













No comments: