11. Inheritance and Polymorphism

11.4. Example: A Toggle Button

The ability to extend an existing class is one of the most powerful fea- Reusing code

tures of object-oriented programming. It allows objects to reuse code defined in the superclasses without having to redefine or recompile the code. As we saw in Chapter 4, a programmer-defined JFrame, such as GreeterGUI, uses the public methods defined for JFrames, Frames, Windows, Containers, Components, and Objects simply because it is a subclass of JFrame (Fig. 4.11). By the same token, it can use all of the public and protected instance variables and constants defined in these classes by simply referring to them in its own code.

In this section, we present an example of how inheritance can be used to extend and customize the functionality of a Java library class. As we saw in Chapter 4, a JButton is a GUI component that can be associated with a particular action by implementing the ActionListener interface. For example, we used a JButton in the GreeterGUI to generate a greeting to the user.

In this section, we will design a more sophisticated button. We will

call it a ToggleButton and define it as a JButton subclass that togglesProblem decomposition

its label whenever it is clicked, in addition to carrying out some kind of associated action.

A light switch behaves similarly to a ToggleButton in this sense. Whenever you flick a light switch, it changes its label from “on” to “off,” but it also turns the lights on or off. Although different switches are asso- ciated with different lights, every light switch toggles its label each time it is clicked. So let’s design a ToggleButton that behaves like a light switch.

The main idea in our design is that a ToggleButton is a JButton that has two labels. By default, a JButton has just a single label. Thus, because of the type of behavior we want to elicit, we need to define ToggleButton as a subclass of JButton with two String variables that will serve as its alternate labels (Fig. 8.8). Note that we give it a construc- tor method that will allow us to provide the initial value of its two label

strings. Another important feature of a ToggleButton is that it should

act as its own ActionListener so that it can toggle its label whenever

 

it is clicked. Therefore, it must also implement the ActionListener

interface.

The complete definition of ToggleButton is given in Figure 8.9. Note how we have defined its constructor. Recall that the JButton class has a constructor method with the signature JButton(String), which allows us to set a JButton’s label during instantiation. We need to do the same thing with one of ToggleButton’s two labels. That is, when we create a ToggleButton, we want to initialize its label to one of its two alternative labels (here, “On” or “Off”).

Because constructor methods are not inherited by the subclass, we want to invoke the superclass’s constructor in the ToggleButton() construc- tor using the super keyword. This must be done as the first statement in


Figure 8.8: A ToggleButton isa

JButton with two labels.

 

 

 

 

 

 

Swapping algorithm

 

 

 

 

 

 

 

 

 

 

 

Swapping values requires a tempo-


the ToggleButton() constructor. By passing l1 to the super construc- tor we are making the first string that the user gives us the default label for our ToggleButton. This will be the label that appears on the button when it is first displayed in a Component.

Notice also in the ToggleButton() constructor that the ToggleButton is designated as its own ActionListener, so whenever it is clicked, its actionPerformed() method will be invoked. The actionPerformed() method exchanges the button’s current label for its other label. Swapping two values in memory is a standard programming practice used in lots of different algorithms. In order to do it properly, you must use a third variable to temporarily store one of the two values you are swapping. The comments in actionPerformed() provide a step-by-step trace of the values of the three variables involved.

 

JAVA PROGRAMMING TIP

Swapping Values. It is necessary to use

a temporary variable whenever you are swapping two values, of any type, in memory. The temporary variable holds the first value while you overwrite it with the second value.

 

The first statement in actionPerformed() creates a temporary String

 

rary variablevariable named tempS and assigns it the value of label1. Recall that label1 was the button’s initial label. To make this example easier to fol- low, let’s suppose that initially label1 is “off” and that label2 is “on.” After line 1 is executed, both tempS and label1 contain “off” as their value. Line 2 then assigns label2’s value to label1. Now both label1

 

,,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

J

Figure 8.9: Definition of the ToggleButton class.

 

and label2 store “on” as their values. In line 3 we assign tempS’s value to label2. Now label2 stores “off” and label1 stores “on,” and we have effectively swapped their original values.

The next time we invoke actionPerformed(), label1 and label2 will have their opposite values initially. Swapping them a second time will assign them their initial values again. We can continue toggling their values in this way indefinitely. To complete the method, the last state- ment in actionPerformed() assigns label1’s current value as the new ToggleButton’s label.

Now that we have seen that a ToggleButton toggles its label be-

tween two values, what about performing an associated action? To do Multiple event handlers

this, we need a design involving multiple event handlers, one to han- dle the toggling of the button’s label and the other to handle its associ- ated action (Fig 8.10). In this design, lightSwitch has two listeners

 

 

 

 

 

 

Listens


 

 

1: generateClickEvent()

2: actionPerformed()


Figure 8.10: The ToggleButton has two ActionListeners. When the button is clicked, the JVM will call each listener’s actionPerformed() method, and each listener will take its own independent action.

 

 

 

 

 

 

 

 

 

that respond to its events: the lightSwitch itself, as a result of the actionPerformed() method in its class, and the ToggleFrame, as a result of actionPerformed() method in this class.

The implementation of this design is given by ToggleFrame, a pro- gram that uses a ToggleButton (Fig. 8.11). Like the GUI we designed in Chapter 4, this program extends the JFrame class and implements the ActionListener interface. In this example we use a ToggleButton to simulate a light switch. Note that we assign the program itself as an ActionListener for the lightSwitch, so that

When lightSwitch is clicked, the program displays the message, “The light is on,” or “The light is off,” in the program’s title bar (Fig. 8.12). This is a somewhat trivial action but it illustrates that a ToggleButton both toggles its own label and carries out some associated action.

The ToggleButton design satisfies several key design principles of object-oriented programming. First and foremost, it uses inheritance to

extend the functionality of the predefined JButton class—the extensibil-Object oriented design principles

ity principle. Secondly, it encapsulates a ToggleButton’s essential be- havior within the ToggleButton class itself—the modularity principle.

 

,,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 8.12: When clicked, ToggleFrame button causes “The light is on” or “The light is off” to appear in the window’s title bar.


J

Figure 8.11: Definition of the ToggleFrame class.

 

 

 

Finally, it hides the mechanism by which a ToggleButton manages its labels—the information-hiding principle.

 

SELF-STUDY EXERCISES

EXERCISE 8.9 Write a code segment (not a whole method) to swap two boolean variables, b1 and b2.

EXERCISE 8.10 Suppose you are designing an GUI that plays a card game, and you want a single button that can be used both to deal the cards and to collect the cards. Write a code segment that creates this type of button, adds it to the JFrame, and designates the JFrame as its ActionListener.