Chapter 9. Interfaces and Polymorphism

CHAPTER GOALS

  • To be able to declare and use interface types

  • To understand the concept of polymorphism

  • To appreciate how interfaces can be used to decouple classes

  • To learn how to implement helper classes as inner classes

    G To implement event listeners in graphical applications

In order to increase programming productivity, we want to be able to reuse software components in multiple projects. However, some adaptations are often required to make reuse possible. In this chapter, you will learn an important strategy for separating the reusable part of a computation from the parts that vary in each reuse scenario. The reusable part invokes methods of an interface. It is combined with a class that implements the interface methods. To produce a different application, you simply plug in another class that implements the same interface. The program's behavior varies according to the implementation that is plugged in—this phenomenon is called polymorphism.

Using Interfaces for Algorithm Reuse

It is often possible to make a service available to a wider set of inputs by focusing on the essential operations that the service requires. Interface types are used to express these common operations.

Consider the DataSet class of Chapter 6. That class provides a service, namely computing the average and maximum of a set of input values. Unfortunately, the class is suitable only for computing the average of a set of numbers. If we wanted to process bank accounts to find the bank account with the highest balance, we could not use the class in its current form. We could modify the class, like this:

public class DataSet // Modified for BankAccount objects
{
   private double sum;
   private BankAccount maximum;
   private int count;
   ...
   public void add(BankAccount x)
   {
      sum = sum + x.getBalance();
      if (count == 0 || maximum.getBalance() < x.getBalance())
          maximum = x;
      count++;
   }
public BankAccount getMaximum()
    {
      return maximum;
    }
}

Or suppose we wanted to find the coin with the highest value among a set of coins. We would need to modify the DataSet class again.

public class DataSet // Modified for Coin objects
{
   private double sum;
   private Coin maximum;
   private int count;
   ...
   public void add(Coin x)
   {
      sum = sum + x.getValue();
      if (count == 0 || maximum.getValue() < x.getValue())
          maximum = x;
      count++;
   }

   public Coin getMaximum()
   {
       return maximum;
   }
}

Clearly, the algorithm for the data analysis service is the same in all cases, but the details of measurement differ. We would like to provide a single class that provides this service to any objects that can be measured.

Suppose that the various classes agree on a method getMeasure that obtains the measure to be used in the data analysis. For bank accounts, getMeasure returns the balance. For coins, getMeasure returns the coin value, and so on. Then we can implement a DataSet class whose add method looks like this:

sum = sum + x.getMeasure();
if (count == 0 || maximum.getMeasure() < x.getMeasure())
   maximum = x;
count++;

What is the type of the variable x? Ideally, x should refer to any class that has a getMeasure method.

In Java, an interface type is used to specify required operations. We will declare an interface type that we call Measurable:

public interface Measurable
{
   double getMeasure();
}

Note

A Java interface type declares methods but does not provide their implementations.

The interface declaration lists all methods that the interface type requires. The Measurable interface type requires a single method, but in general, an interface type can require multiple methods.

Note that the Measurable type is not a type in the standard library—it is a type that was created specifically for this book, in order to make the DataSet class more reusable.

An interface type is similar to a class, but there are several important differences:

  • All methods in an interface type are abstract; that is, they have a name, parameters, and a return type, but they don't have an implementation.

  • All methods in an interface type are automatically public.

  • An interface type does not have instance variables.

Note

Unlike a class, an interface type provides no implementation.

Now we can use the interface type Measurable to declare the variables x and maximum.

public class DataSet
{
   private double sum;
   private Measurable maximum;
   private int count;
   ...
   public void add(Measurable x)
   {
      sum = sum + x.getMeasure();
      if (count == 0 || maximum.getMeasure() < x.getMeasure())
         maximum = x;
      count++;
   }

   public Measurable getMaximum()
   {
      return maximum;
   }
}

This DataSet class is usable for analyzing objects of any class that implements the Measurable interface. A class implements an interface type if it declares the interface in an implements clause. It should then implement the method or methods that the interface requires.

public class BankAccount implements Measurable
{
   ...
   public double getMeasure()
   {
      return balance;
   }
}

Note

Use the implements reserved word to indicate that a class implements an interface type.

Attachments Conform to the Mixer's Interface

Figure 9.1. Attachments Conform to the Mixer's Interface

Note that the class must declare the method as public, whereas the interface need not—all methods in an interface are public.

Similarly, it is an easy matter to modify the Coin class to implement the Measurable interface.

public class Coin implements Measurable
{
   public double getMeasure()
   {
      return value;
   }
   ...
}

In summary, the Measurable interface expresses what all measurable objects have in common. This commonality makes the flexibility of the improved DataSet class possible. A data set can analyze objects of any class that implements the Measurable interface.

This is a typical usage for interface types. A service provider—in this case, the DataSet—specifies an interface for participating in the service. Any class that conforms to that interface can then be used with the service. This is similar to the way a mixer will provide rotation to any attachment that fits its interface (see Figure 1).

Note

Use interface types to make code more reusable.

UML Diagram of the DataSet Class and the Classes that Implement the Measurable Interface

Figure 9.2. UML Diagram of the DataSet Class and the Classes that Implement the Measurable Interface

Figure 2 shows the relationships between the DataSet class, the Measurable inter-face, and the classes that implement the interface. Note that the DataSet class depends only on the Measurable interface. It is decoupled from the BankAccount and Coin classes.

In the UML notation, interfaces are tagged with an indicator «interface». A dotted arrow with a triangular tip denotes the "is-a" relationship between a class and an interface. You have to look carefully at the arrow tips—a dotted line with an open arrow tip (→) denotes the "uses" relationship or dependency.

ch09/measure1/DataSetTester.java

1 /**
2    This program tests the DataSet class.
3 */
4 public class DataSetTester
5 {
6    public static void main(String[] args)
7    {
8       DataSet bankData = new DataSet();
9
10       bankData.add(new BankAccount(0));
11       bankData.add(new BankAccount(10000));
12       bankData.add(new BankAccount(2000));
13
14       System.out.println("Average balance: " + bankData.getAverage());
15       System.out.println("Expected: 4000");
16       Measurable max = bankData.getMaximum();
17       System.out.println("Highest balance: " + max.getMeasure());
18       System.out.println("Expected: 10000");
19
20       DataSet coinData = new DataSet();
21
22       coinData.add(new Coin(0.25, "quarter"));
23       coinData.add(new Coin(0.1, "dime"));
24       coinData.add(new Coin(0.05, "nickel"));
25
26         System.out.println("Average coin value: " + coinData.getAverage());
27         System.out.println("Expected: 0.133");
28         max = coinData.getMaximum();
29         System.out.println("Highest coin value: " + max.getMeasure());
30         System.out.println("Expected: 0.25");
31      }
32   }

Program Run

Average balance: 4000.0
Expected: 4000
Highest balance: 10000.0
Expected: 10000
Average coin value: 0.13333333333333333
Expected: 0.133
Highest coin value: 0.25
Expected: 0.25
UML Diagram of the DataSet Class and the Classes that Implement the Measurable Interface

2. Why can't the add method of the DataSet class have a parameter of type Object?

Converting Between Class and Interface Types

Interfaces are used to express the commonality between classes. In this section, we discuss when it is legal to convert between class and interface types.

Have a close look at the call

bankData.add(new BankAccount(1000));

from the test program of the preceding section. Here we pass an object of type BankAccount to the add method of the DataSet class. However, that method has a parameter of type Measurable:

public void add(Measurable x)

It it legal to convert from the BankAccount type to the Measurable type. In general, you can convert from a class type to the type of any interface that the class implements. For example,

BankAccount account = new BankAccount(1000);
Measurable meas = account; // OK

Note

You can convert from a class type to an interface type, provided the class implements the interface.

Alternatively, a Measurable variable can refer to an object of the Coin class of the preceding section because that class also implements the Measurable interface.

Coin dime = new Coin(0.1, "dime");
Measurable meas = dime; // Also OK

However, the Rectangle class from the standard library doesn't implement the Measurable interface. Therefore, the following assignment is an error:

Measurable meas = new Rectangle(5, 10, 20, 30); // Error
Variables of Class and Interface Types

Figure 9.3. Variables of Class and Interface Types

Occasionally, it happens that you store an object in an interface reference and you need to convert its type back. This happens in the getMaximum method of the DataSet class. The DataSet stores the object with the largest measure, as a Measurable reference.

DataSet coinData = new DataSet();
coinData.add(new Coin(0.25, "quarter"));
coinData.add(new Coin(0.1, "dime"));
coinData.add(new Coin(0.05, "nickel"));
Measurable max = coinData.getMaximum();

Now what can you do with the max reference? You know it refers to a Coin object, but the compiler doesn't. For example, you cannot call the getName method:

String coinName = max.getName(); // Error

That call is an error, because the Measurable type has no getName method.

However, as long as you are absolutely sure that max refers to a Coin object, you can use the cast notation to convert its type back:

Coin maxCoin = (Coin) max;
String name = maxCoin.getName();

If you are wrong, and the object doesn't actually refer to a coin, a run-time exception will occur.

This cast notation is the same notation that you saw in Chapter 4 to convert between number types. For example, if x is a floating-point number, then (int) x is the integer part of the number. The intent is similar—to convert from one type to another. However, there is one big difference between casting of number types and casting of class types. When casting number types, you may lose information, and you use the cast to tell the compiler that you agree to the potential information loss. When casting object types, on the other hand, you take a riskof causing an exception, and you tell the compiler that you agree to that risk.

Variables of Class and Interface Types

4. If both BankAccount and Coin implement the Measurable interface, can a Coin reference be converted to a BankAccount reference?

Polymorphism

When multiple classes implement the same interface, each class can implement the methods of the interface in different ways. How is the correct method executed when the interface method is invoked? We will answer that question in this section.

It is worth emphasizing once again that it is perfectly legal—and in fact very common—to have variables whose type is an interface, such as Measurable meas;

Just remember that the object to which meas refers doesn't have type Measurable. In fact, no object has type Measurable. Instead, the type of the object is some class that implements the Measurable interface. This might be an object of the BankAccount or Coin class, or some other class with a getMeasure method.

meas = new BankAccount(1000); // OK
meas = new Coin(0.1, "dime"); // OK

What can you do with an interface variable, given that you don't know the class of the object that it references? You can invoke the methods of the interface:

double m = meas.getMeasure();

The DataSet class took advantage of this capability by computing the measure of the added object, without knowing exactly what kind of object was added.

Now let's think through the call to the getMeasure method more carefully. Which getMeasure method? The BankAccount and Coin classes provide two different implementations of that method. How did the correct method get called if the caller didn't even know the exact class to which meas belongs?

The Java virtual machine locates the correct method by first looking at the class of the actual object, and then calling the method with the given name in that class. That is, if meas refers to a BankAccount object, then the BankAccount.getMeasure method is called. If meas refers to a Coin object, then the Coin.getMeasure method is called. This means that one method call

double m = meas.getMeasure();

can invoke different methods depending on the momentary contents of meas. This mechanism for locating the appropriate method is called dynamic method lookup.

Note

When the virtual machine calls an instance method, it locates the method of the implicit parameter's class. This is called dynamic method lookup.

Dynamic method lookup enables a programming technique called polymorphism. The term "polymorphism" comes from the Greek words for "many shapes". The same computation works for objects of many shapes, and adapts itself to the nature of the objects.

Note

Polymorphism denotes the ability to treat objects with differences in behavior in a uniform way.

An Interface Reference Can Refer to an Object of Any Class that Implements the Interface

Figure 9.4. An Interface Reference Can Refer to an Object of Any Class that Implements the Interface

An Interface Reference Can Refer to an Object of Any Class that Implements the Interface

6. Why can you nevertheless declare a variable whose type is Measurable?

7. What does this code fragment print? Why is this an example of polymorphism?

DataSet data = new DataSet();
data.add(new BankAccount(1000));
data.add(new Coin(0.1, "dime"));
System.out.println(data.getAverage());

Using Interfaces for Callbacks

In this section, we introduce the notion of a callback, show how it leads to a more flexible DataSet class, and study how a callback can be implemented in Java by using interface types.

To understand why a further improvement to the DataSet class is desirable, consider these limitations of the Measurable interface:

  • You can add the Measurable interface only to classes under your control. If you want to process a set of Rectangle objects, you cannot make the Rectangle class implement another interface—it is a system class, which you cannot change.

  • You can measure an object in only one way. If you want to analyze a set of savings accounts both by bank balance and by interest rate, you are stuck.

Therefore, let's rethink the DataSet class. The data set needs to measure the objects that are added. When the objects are required to be of type Measurable, the responsibility of measuring lies with the added objects themselves, which is the cause of the limitations that we noted.

It would be better if we could give a method for measuring objects to a data set. When collecting rectangles, we might give it a method for computing the area of a rectangle. When collecting savings accounts, we might give it a method for getting the account's interest rate.

Such a method is called a callback. A callback is a mechanism for bundling up a block of code so that it can be invoked at a later time.

In some programming languages, it is possible to specify callbacks directly, as blocks of code or names of methods. But Java is an object-oriented programming language. Therefore, you turn callbacks into objects. This process starts by declaring an interface for the callback:

public interface Measurer
{
   double measure(Object anObject);
}

Note

A callback is a mechanism for specifying code that is executed at a later time.

The measure method measures an object and returns its measurement. Here we use the fact that all objects can be converted to the type Object, the "lowest common denominator" of all classes in Java. We will discuss the Object type in greater detail in Chapter 10.

The code that makes the call to the callback receives an object of a class that implements this interface. In our case, the improved DataSet class is constructed with a Measurer object (that is, an object of some class that implements the Measurer interface). That object is saved in a measurer instance variable.

public DataSet(Measurer aMeasurer)
{
   sum = 0;
   count = 0;
   maximum = null;
   measurer = aMeasurer;
}

The measurer variable is used to carry out the measurements, like this:

public void add(Object x)
{
   sum = sum + measurer.measure(x);
   if (count == 0 || measurer.measure(maximum) < measurer.measure(x))
      maximum = x;
   count++;
}

The DataSet class simply makes a callback to the measure method whenever it needs to measure any object.

Finally, a specific callback is obtained by implementing the Measurer interface. For example, here is how you can measure rectangles by area. Provide a class

public class RectangleMeasurer implements Measurer
{
   public double measure(Object anObject)
   {
      Rectangle aRectangle = (Rectangle) anObject;
      double area = aRectangle.getWidth() * aRectangle.getHeight();
      return area;
   }
}

Note that the measure method must accept a parameter of type Object, even though this particular measurer just wants to measure rectangles. The method parameter types must match those of the measure method in the Measurer interface. Therefore, the Object parameter is cast to the Rectangle type:

Rectangle aRectangle = (Rectangle) anObject;

What can you do with a RectangleMeasurer? You need it for a DataSet that compares rectangles by area. Construct an object of the RectangleMeasurer class and pass it to the DataSet constructor.

Measurer m = new RectangleMeasurer();
DataSet data = new DataSet(m);

Next, add rectangles to the data set.

data.add(new Rectangle(5, 10, 20, 30));
data.add(new Rectangle(10, 20, 30, 40));
...
UML Diagram of the DataSet Class and the Measurer Interface

Figure 9.5. UML Diagram of the DataSet Class and the Measurer Interface

The data set will ask the RectangleMeasurer object to measure the rectangles. In other words, the data set uses the RectangleMeasurer object to carry out callbacks.

Figure 5 shows the UML diagram of the classes and interfaces of this solution. As in Figure 2, the DataSet class is decoupled from the Rectangle class whose objects it processes. However, unlike in Figure 2, the Rectangle class is no longer coupled with another class. Instead, to process rectangles, you provide a small "helper" class RectangleMeasurer. This helper class has only one purpose: to tell the DataSet how to measure its objects.

ch09/measure2/Measurer.java

1 /**
2    Describes any class whose objects can measure other objects.
3 */
4 public interface Measurer
5 {
6    /**
7       Computes the measure of an object.
8       @param anObject the object to be measured
9       @return the measure
10   */
11   double measure(Object anObject);
12 }

ch09/measure2/RectangleMeasurer.java

1 import java.awt.Rectangle;
2
3 /**
4    Objects of this class measure rectangles by area.
5 */
6 public class RectangleMeasurer implements Measurer
7 {
8    public double measure(Object anObject)
9    {
10       Rectangle aRectangle = (Rectangle) anObject;
11       double area = aRectangle.getWidth() * aRectangle.getHeight();
12       return area;
13    }
14 }

ch09/measure2/DataSet.java

1 /**
2    Computes the average of a set of data values.
3 */
4 public class DataSet
5 {
6    private double sum;
7    private Object maximum;
8    private int count;
9    private Measurer measurer;
10
11    /**
12       Constructs an empty data set with a given measurer.
13       @param aMeasurer the measurer that is used to measure data values
14     */
15    public DataSet(Measurer aMeasurer)
16    {
17       sum = 0;
18       count = 0;
19       maximum = null;
20       measurer = aMeasurer;
21    }
22
23    /**
24       Adds a data value to the data set.
25       @param x a data value
26    */
27    public void add(Object x)
28    {
29       sum = sum + measurer.measure(x);
30       if (count == 0 || measurer.measure(maximum) < measurer.measure(x))
31           maximum = x;
32       count++;
33    }
34
35    /**
36       Gets the average of the added data.
37       @return the average or 0 if no data has been added
38    */
39    public double getAverage()
40    {
41       if (count == 0) return 0;
42       else return sum / count;
43    }
44
45    /**
46       Gets the largest of the added data.
47       @return the maximum or 0 if no data has been added
48    */
49    public Object getMaximum()
50    {
51        return maximum;
52    }
53 }

ch09/measure2/DataSetTester2.java

1   import java.awt.Rectangle;
2
3 /**
4    This program demonstrates the use of a Measurer.
5 */
6 public class DataSetTester2
7 {
8    public static void main(String[] args)
9    {
10       Measurer m = new RectangleMeasurer();
11
12       DataSet data = new DataSet(m);
13
14       data.add(new Rectangle(5, 10, 20, 30));
15       data.add(new Rectangle(10, 20, 30, 40));
16       data.add(new Rectangle(20, 30, 5, 15));
17
18       System.out.println("Average area: " + data.getAverage());
19       System.out.println("Expected: 625");
20
21       Rectangle max = (Rectangle) data.getMaximum();
22       System.out.println("Maximum area rectangle: " + max);
23       System.out.println("Expected: "
24             + "java.awt.Rectangle[x=10,y=20,width=30,height=40]");
25    }
26 }

Program Run

Average area: 625
Expected: 625
Maximum area rectangle: java.awt.Rectangle[x=10,y=20,width=30,height=40]
Expected: java.awt.Rectangle[x=10,y=20,width=30,height=40]
UML Diagram of the DataSet Class and the Measurer Interface

9. How can you use the DataSet class of this section to find the longest String from a set of inputs?

10. Why does the measure method of the Measurer interface have one more parameter than the getMeasure method of the Measurable interface?

Inner Classes

The RectangleMeasurer class is a very trivial class. We need this class only because the DataSet class needs an object of some class that implements the Measurer interface. When you have a class that serves a very limited purpose, such as this one, you can declare the class inside the method that needs it:

public class DataSetTester3
{
   public static void main(String[] args)
   {
      class RectangleMeasurer implements Measurer
      {
         ...
}

      Measurer m = new RectangleMeasurer();
      DataSet data = new DataSet(m);
      ...
   }
}

A class that is declared inside another class, such as the RectangleMeasurer class in this example, is called an inner class. This arrangement signals to the reader of your program that the RectangleMeasurer class is not interesting beyond the scope of this method. Since an inner class inside a method is not a publicly accessible feature, you don't need to document it as thoroughly.

Note

An inner class is declared inside another class.

You can also declare an inner class inside an enclosing class, but outside of its methods. Then the inner class is available to all methods of the enclosing class.

Note

Inner classes are commonly used for utility classes that should not be visible elsewhere in a program.

public class DataSetTester3
{
   class RectangleMeasurer implements Measurer
   {
       ...
   }

   public static void main(String[] args)
   {

      Measurer m = new RectangleMeasurer();
      DataSet data = new DataSet(m);
       ...
   }
}

When you compile the source files for a program that uses inner classes, have a look at the class files in your program directory—you will find that the inner classes are stored in files with curious names, such as DataSetTester3$1RectangleMeasurer.class. The exact names aren't important. The point is that the compiler turns an inner class into a regular class file.

ch09/measure3/DataSetTester3.java

1 import java.awt.Rectangle;
2
3 /**
4    This program demonstrates the use of an inner class.
5 */
6 public class DataSetTester3
7 {
8    public static void main(String[] args)
9    {
10       class RectangleMeasurer implements Measurer
11       {
12          public double measure(Object anObject)
13          {
14             Rectangle aRectangle = (Rectangle) anObject;
15              double area
16                   = aRectangle.getWidth() * aRectangle.getHeight();
17              return area;
18          }
19       }
20
21         Measurer m = new RectangleMeasurer();
22
23         DataSet data = new DataSet(m);
24
25         data.add(new Rectangle(5, 10, 20, 30));
26         data.add(new Rectangle(10, 20, 30, 40));
27         data.add(new Rectangle(20, 30, 5, 15));
28
29         System.out.println("Average area: " + data.getAverage());
30         System.out.println("Expected: 625");
31
32         Rectangle max = (Rectangle) data.getMaximum();
33         System.out.println("Maximum area rectangle: " + max);
34         System.out.println("Expected: "
35               + "java.awt.Rectangle[x=10,y=20,width=30,height=40]");
36      }
37   }
Inner Classes

12. How many class files are produced when you compile the DataSetTester3 program?

Mock Objects

When you work on a program that consists of multiple classes, you often want to test some of the classes before the entire program has been completed. A very effective technique for this purpose is the use of mock objects. A mock object provides the same services as another object, but in a simplified manner.

Consider a grade book application that manages quiz scores for students. This calls for a class GradeBook with methods such as

public void addScore(int studentId, double score)
public double getAverageScore(int studentId)
public void save(String filename)

Note

A mock object provides the same services as another object, but in a simplified manner.

Now consider the class GradingProgram that manipulates a GradeBook object. That class calls the methods of the GradeBook class. We would like to test the GradingProgram class without having a fully functional GradeBook class.

To make this work, declare an interface type with the same methods that the GradeBook class provides. A common convention is to use the letter I as the prefix for such an interface:

public interface IGradeBook
{
   void addScore(int studentId, double score);
   double getAverageScore(int studentId);
   void save(String filename);
   ...
}

The GradingProgram class should only use this interface, never the GradeBook class. Of course, the GradeBook class implements this interface, but as already mentioned, it may not be ready for some time.

In the meantime, provide a mock implementation that makes some simplifying assumptions. Saving is not actually necessary for testing the user interface. We can temporarily restrict to the case of a single student.

public class MockGradeBook implements IGradeBook
{
   private ArrayList<Double> scores;

   public void addScore(int studentId, double score)
   {
      // Ignore studentId
      scores.add(score);
   }
   double getAverageScore(int studentId)
   {
      double total = 0;
      for (double x : scores) { total = total + x; }
      return total / scores.size();
   }
   void save(String filename)
   {
      // Do nothing
   }
   ...
}

Note

Both the mock class and the actual class implement the same interface.

Now construct an instance of MockGradeBook and use it in the GradingProgram class. You can immediately test the GradingProgram class. When you are ready to test the actual class, simply use a GradeBook instance instead. Don't erase the mock class—it will still come in handy for regression testing.

Mock Objects

14. Why is the technique of mock objects particularly effective when the GradeBook and GradingProgram class are developed by two programmers?

Events, Event Sources, and Event Listeners

This and the following sections continue the book's graphics track. You will learn how interfaces are used when programming graphical user interfaces.

In the applications that you have written so far, user input was under control of the program. The program asked the user for input in a specific order. For example, a program might ask the user to supply first a name, then a dollar amount. But the programs that you use every day on your computer don't work like that. In a program with a graphical user interface, the user is in control. The user can use both the mouse and the keyboard and can manipulate many parts of the user interface in any desired order. For example, the user can enter information into text fields, pull down menus, click buttons, and drag scroll bars in any order. The program must react to the user commands, in whatever order they arrive. Having to deal with many possible inputs in random order is quite a bit harder than simply forcing the user to supply input in a fixed order.

In the following sections, you will learn how to write Java programs that can react to user-interface events, such as menu selections and mouse clicks. The Java windowing toolkit has a very sophisticated mechanism that allows a program to specify the events in which it is interested and which objects to notify when one of these events occurs.

Note

User-interface events include key presses, mouse moves, button clicks, menu selections, and so on.

Whenever the user of a graphical program types characters or uses the mouse anywhere inside one of the windows of the program, the Java windowing toolkit sends a notification to the program that an event has occurred. The windowing toolkit generates huge numbers of events. For example, whenever the mouse moves a tiny interval over a window, a "mouse move" event is generated. Whenever the mouse button is clicked, a "mouse pressed" and a "mouse released" event are generated. In addition, higher level events are generated when a user selects a menu item or button.

Most programs don't want to be flooded by boring events. For example, consider what happens when selecting a menu item with the mouse. The mouse moves over the menu item, then the mouse button is pressed, and finally the mouse button is released. Rather than receiving lots of irrelevant mouse events, a program can indicate that it only cares about menu selections, not about the underlying mouse events. However, if the mouse input is used for drawing shapes on a virtual canvas, it is necessary to closely track mouse events.

Note

An event listener belongs to a class that is provided by the application programmer. Its methods describe the actions to be taken when an event occurs.

Every program must indicate which events it needs to receive. It does that by installing event listener objects. An event listener object belongs to a class that you provide. The methods of your event listener classes contain the instructions that you want to have executed when the events occur.

To install a listener, you need to know the event source. The event source is the user-interface component that generates a particular event. You add an event listener object to the appropriate event sources. Whenever the event occurs, the event source calls the appropriate methods of all attached event listeners.

Note

Event sources report on events. When an event occurs, the event source notifies all event listeners.

This sounds somewhat abstract, so let's run through an extremely simple program that prints a message whenever a button is clicked (see Figure 6).

Implementing an Action Listener

Figure 9.6. Implementing an Action Listener

Button listeners must belong to a class that implements the ActionListener interface:

public interface ActionListener
{
   void actionPerformed(ActionEvent event);
}

Note

Use JButton components for buttons. Attach an ActionListener to each button.

This particular interface has a single method, actionPerformed. It is your job to supply a class whose actionPerformed method contains the instructions that you want executed whenever the button is clicked. Here is a very simple example of such a listener class:

ch09/button1/ClickListener.java

1 import java.awt.event.ActionEvent;
2 import java.awt.event.ActionListener;
3
4 /**
5    An action listener that prints a message.
6 */
7 public class ClickListener implements ActionListener
8 {
9    public void actionPerformed(ActionEvent event)
10    {
11       System.out.println("I was clicked.");
12    }
13 }

We ignore the event parameter of the actionPerformed method—it contains additional details about the event, such as the time at which it occurred.

Once the listener class has been declared, we need to construct an object of the class and add it to the button:

ActionListener listener = new ClickListener();
button.addActionListener(listener);

Whenever the button is clicked, it calls

listener.actionPerformed(event);

As a result, the message is printed.

You can think of the actionPerformed method as another example of a callback, similar to the measure method of the Measurer class. The windowing toolkit calls the actionPerformed method whenever the button is pressed, whereas the DataSet calls the measure method whenever it needs to measure an object.

The ButtonViewer class, whose source code is provided at the end of this section, constructs a frame with a button and adds a ClickListener to the button. You can test this program out by opening a console window, starting the ButtonViewer program from that console window, clicking the button, and watching the messages in the console window.

ch09/button1/ButtonViewer.java

1 import java.awt.event.ActionListener;
2 import javax.swing.JButton;
3 import javax.swing.JFrame;
4
5 /**
6    This program demonstrates how to install an action listener.
7 */
8 public class ButtonViewer
9 {
10    private static final int FRAME_WIDTH = 100;
11    private static final int FRAME_HEIGHT = 60;
12
13    public static void main(String[] args)
14    {
15       JFrame frame = new JFrame();
16       JButton button = new JButton("Click me!");
17       frame.add(button);
18
19       ActionListener listener = new ClickListener();
20       button.addActionListener(listener);
21
22       frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
23       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
24       frame.setVisible(true);
25    }
26 }
Implementing an Action Listener

16. Why is it legal to assign a ClickListener object to a variable of type ActionListener?

Using Inner Classes for Listeners

In the preceding section, you saw how the code that is executed when a button is clicked is placed into a listener class. It is common to implement listener classes as inner classes like this:

JButton button = new JButton("...");

// This inner class is declared in the same method as the button variable
class MyListener implements ActionListener
{
   ...
};

ActionListener listener = new MyListener();
button.addActionListener(listener);

There are two reasons for this arrangement. The trivial listener class is located exactly where it is needed, without cluttering up the remainder of the project. Moreover, inner classes have a very attractive feature: Their methods can access variables that are declared in surrounding blocks. In this regard, method declarations of inner classes behave similarly to nested blocks.

Recall that a block is a statement group enclosed by braces. If a block is nested inside another, the inner block has access to all variables from the surrounding block:

{// Surrounding block
   BankAccount account = new BankAccount();
   if (...)
   {// Inner block
      ...
      // OK to access variable from surrounding block
      account.deposit(interest);
     ...
   }  // End of inner block
   ...
}  // End of surrounding block

The same nesting works for inner classes. Except for some technical restrictions, which we will examine later in this section, the methods of an inner class can access the variables from the enclosing scope. This feature is very useful when implementing event handlers. It allows the inner class to access variables without having to pass them as constructor or method parameters.

Let's look at an example. Suppose we want to add interest to a bank account whenever a button is clicked.

Note

Methods of an inner class can access local and instance variables from the surrounding scope.

JButton button = new JButton("Add Interest");
final BankAccount account = new BankAccount(INITIAL_BALANCE);

// This inner class is declared in the same method as the account and button variables.
class AddInterestListener implements ActionListener
{
   public void actionPerformed(ActionEvent event)
   {
      // The listener method accesses the account variable
      // from the surrounding block
      double interest = account.getBalance() * INTEREST_RATE / 100;
      account.deposit(interest);
   }
};

ActionListener listener = new AddInterestListener();
button.addActionListener(listener);

There is a technical wrinkle. An inner class can access surrounding localvariables only if they are declared as final. That sounds like a restriction, but it is usually not an issue in practice. Keep in mind that an object variable is final when the variable always refers to the same object. The state of the object can change, but the variable can't refer to a different object. For example, in our program, we never intended to have the account variable refer to multiple bank accounts, so there was no harm in declaring it as final.

Note

Local variables that are accessed by an inner class method must be declared as final.

An inner class can also access instance variables of the surrounding class, again with a restriction. The instance variable must belong to the object that constructed the inner class object. If the inner class object was created inside a static method, it can only access static variables.

Here is the source code for the program.

ch09/button2/InvestmentViewer1.java

1 import java.awt.event.ActionEvent;
2 import java.awt.event.ActionListener;
3 import javax.swing.JButton;
4 import javax.swing.JFrame;
5
6 /**
7    This program demonstrates how an action listener can access
8    a variable from a surrounding block.
9 */
10 public class InvestmentViewer1
11 {
12    private static final int FRAME_WIDTH = 120;
13    private static final int FRAME_HEIGHT = 60;
14
15    private static final double INTEREST_RATE = 10;
16    private static final double INITIAL_BALANCE = 1000;
17
18    public static void main(String[] args)
19    {
20       JFrame frame = new JFrame();
21
22       // The button to trigger the calculation
23       JButton button = new JButton("Add Interest");
24       frame.add(button);
25
26         //  The application adds interest to this bank account
27              final BankAccount account = new BankAccount(INITIAL_BALANCE);
28
29         class AddInterestListener implements ActionListener
30         {
31            public void actionPerformed(ActionEvent event)
32            {
33               // The listener method accesses the account variable
34               // from the surrounding block
35               double interest = account.getBalance() * INTEREST_RATE / 100;
36               account.deposit(interest);
37               System.out.println("balance: " + account.getBalance());
38            }
39          }
40
41         ActionListener listener = new AddInterestListener();
42         button.addActionListener(listener);
43
44         frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
45         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
46         frame.setVisible(true);
47      }
48   }

Program Run

balance: 1100.0
balance: 1210.0
balance: 1331.0
balance: 1464.1
Using Inner Classes for Listeners

18. If an inner class accesses a local variable from a surrounding scope, what special rule applies?

Building Applications with Buttons

In this section, you will learn how to structure a graphical application that contains buttons. We will put a button to work in our simple investment viewer program. Whenever the button is clicked, interest is added to a bank account, and the new balance is displayed (see Figure 7).

First, we construct an object of the JButton class. Pass the button label to the constructor:

JButton button = new JButton("Add Interest");

We also need a user-interface component that displays a message, namely the current bank balance. Such a component is called a label. You pass the initial message string to the JLabel constructor, like this:

JLabel label = new JLabel("balance: " + account.getBalance());

The frame of our application contains both the button and the label. However, we cannot simply add both components directly to the frame—they would be placed on top of each other. The solution is to put them into a panel, a container for other user-interface components, and then add the panel to the frame:

JPanel panel = new JPanel();
panel.add(button);
panel.add(label);
frame.add(panel);

Note

Use a JPanel container to group multiple user-interface components together.

An Application with a Button

Figure 9.7. An Application with a Button

Now we are ready for the hard part—the event listener that handles button clicks. As in the preceding section, it is necessary to provide a class that implements the ActionListener interface, and to place the button action into the actionPerformed method. Our listener class adds interest and displays the new balance:

class AddInterestListener implements ActionListener
{
   public void actionPerformed(ActionEvent event)
   {
      double interest = account.getBalance() * INTEREST_RATE / 100;
      account.deposit(interest);
      label.setText("balance: " + account.getBalance());
   }
}

Note

You specify button click actions through classes that implement the ActionListener interface.

There is just a minor technicality. The actionPerformed method manipulates the account and label variables. These are local variables of the main method of the investment viewer program, not instance variables of the AddInterestListener class. We therefore need to declare the account and label variables as final so that the action-Performed method can access them.

Let's put the pieces together.

public static void main(String[] args)
{
    ...
   JButton button = new JButton("Add Interest");
   final BankAccount account = new BankAccount(INITIAL_BALANCE);
   final JLabel label = new JLabel("balance: " + account.getBalance());
   class AddInterestListener implements ActionListener
   {
      public void actionPerformed(ActionEvent event)
      {
         double interest = account.getBalance() * INTEREST_RATE / 100;
         account.deposit(interest);
         label.setText("balance: " + account.getBalance());
      }
   }

   ActionListener listener = new AddInterestListener();
   button.addActionListener(listener);
    ...
}

With a bit of practice, you will learn to glance at this code and translate it into plain English: "When the button is clicked, add interest and set the label text."

Here is the complete program. It demonstrates how to add multiple components to a frame, by using a panel, and how to implement listeners as inner classes.

ch09/button3/InvestmentViewer2.java

1 import java.awt.event.ActionEvent;
2 import java.awt.event.ActionListener;
3 import javax.swing.JButton;
4 import javax.swing.JFrame;
5 import javax.swing.JLabel;
6 import javax.swing.JPanel;
7 import javax.swing.JTextField;
8
9 /**
10    This program displays the growth of an investment.
11 */
12 public class InvestmentViewer2
13 {
14    private static final int FRAME_WIDTH = 400;
15    private static final int FRAME_HEIGHT = 100;
16
17    private static final double INTEREST_RATE = 10;
18    private static final double INITIAL_BALANCE = 1000;
19
20    public static void main(String[] args)
21    {
22       JFrame frame = new JFrame();
23
24       // The button to trigger the calculation
25       JButton button = new JButton("Add Interest");
26
27       // The application adds interest to this bank account
28       final BankAccount account = new BankAccount(INITIAL_BALANCE);
29
30       // The label for displaying the results
31       final JLabel label = new JLabel("balance: " + account.getBalance());
32
33       // The panel that holds the user-interface components
34       JPanel panel = new JPanel();
35       panel.add(button);
36       panel.add(label);
37       frame.add(panel);
38
39       class AddInterestListener implements ActionListener
40       {
41          public void actionPerformed(ActionEvent event)
42          {
43             double interest = account.getBalance() * INTEREST_RATE / 100;
44             account.deposit(interest);
45             label.setText("balance: " + account.getBalance());
46          }
47       }
48
49       ActionListener listener = new AddInterestListener();
50       button.addActionListener(listener);
51
52        frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
53        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
54        frame.setVisible(true);
55      }
56   }
An Application with a Button

20. Why was it not necessary to declare the button variable as final?

Processing Timer Events

In this section we will study timer events and show how they allow you to implement simple animations.

The Timer class in the javax.swing package generates a sequence of action events, spaced apart at even time intervals. (You can think of a timer as an invisible button that is automatically clicked.) This is useful whenever you want to have an object updated in regular intervals. For example, in an animation, you may want to update a scene ten times per second and redisplay the image, to give the illusion of movement.

When you use a timer, you specify the frequency of the events and an object of a class that implements the ActionListener interface. Place whatever action you want to occur inside the actionPerformed method. Finally, start the timer.

class MyListener implements ActionListener
{
   public void actionPerformed(ActionEvent event)
    {
      Action that is executed at each timer event
    }
}

MyListener listener = new MyListener();
Timer t = new Timer(interval, listener);
t.start();

Note

A timer generates timer events at fixed intervals.

Then the timer calls the actionPerformed method of the listener object every interval milliseconds.

Our sample program will display a moving rectangle. We first supply a Rectangle-Component class with a moveBy method that moves the rectangle by a given amount.

ch09/timer/RectangleComponent.java

1 import java.awt.Graphics;
2 import java.awt.Graphics2D;
3 import java.awt.Rectangle;
4 import javax.swing.JComponent;
5
6 /**
7    This component displays a rectangle that can be moved.
8 */
9 public class RectangleComponent extends JComponent
10 {
11    private static final int BOX_X = 100;
12    private static final int BOX_Y = 100;
13    private static final int BOX_WIDTH = 20;
14    private static final int BOX_HEIGHT = 30;
15
16    private Rectangle box;
17
18    public RectangleComponent()
19    {
20       // The rectangle that the paint method draws
21       box = new Rectangle(BOX_X, BOX_Y, BOX_WIDTH, BOX_HEIGHT);
22    }
23
24    public void paintComponent(Graphics g)
25    {
26       Graphics2D g2 = (Graphics2D) g;
27
28       g2.draw(box);
29    }
30
31    /**
32       Moves the rectangle by a given amount.
33       @param x the amount to move in the x-direction
34       @param y the amount to move in the y-direction
35     */
36     public void moveBy(int dx, int dy)
37     {
38       box.translate(dx, dy);
39       repaint();
40     }
41 }

Note the call to repaint in the moveBy method. This call is necessary to ensure that the component is repainted after the state of the rectangle object has been changed. Keep in mind that the component object does not contain the pixels that show the drawing. The component merely contains a Rectangle object, which itself contains four coordinate values. Calling translate updates the rectangle coordinate values. The call to repaint forces a call to the paintComponent method. The paintComponent method redraws the component, causing the rectangle to appear at the updated location.

Note

The repaint method causes a component to repaint itself. Call repaint whenever you modify the shapes that the paintComponent method draws.

The actionPerformed method of the timer listener simply calls component.moveBy(1, 1). This moves the rectangle one pixel down and to the right. Since the actionPer-formed method is called many times per second, the rectangle appears to move smoothly across the frame.

ch09/timer/RectangleMover.java

1 import java.awt.event.ActionEvent;
2 import java.awt.event.ActionListener;
3 import javax.swing.JFrame;
4 import javax.swing.Timer;
5
6 /**
7    This program moves the rectangle.
8 */
9 public class RectangleMover
10 {
11    private static final int FRAME_WIDTH = 300;
12    private static final int FRAME_HEIGHT = 400;
13
14    public static void main(String[] args)
15    {
16       JFrame frame = new JFrame();
17
18       frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
19       frame.setTitle("An animated rectangle");
20       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
21
22       final RectangleComponent component = new RectangleComponent();
23       frame.add(component);
24
25       frame.setVisible(true);
26
27       class TimerListener implements ActionListener
28       {
29          public void actionPerformed(ActionEvent event)
30          {
31             component.moveBy(1, 1);
32          }
33       }
34
35       ActionListener listener = new TimerListener();
36
37       final int DELAY = 100; // Milliseconds between timer ticks
38       Timer t = new Timer(DELAY, listener);
39       t.start();
40    }
41 }
Processing Timer Events

22. What would happen if you omitted the call to repaint in the moveBy method?

Mouse Events

If you write programs that show drawings, and you want users to manipulate the drawings with a mouse, then you need to process mouse events. Mouse events are more complex than button clicks or timer ticks.

Note

You use a mouse listener to capture mouse events.

A mouse listener must implement the MouseListener interface, which contains the following five methods:

public interface MouseListener
{
   void mousePressed(MouseEvent event);
       //  Called when a mouse button has been pressed on a component
   void mouseReleased(MouseEvent event);
       //  Called when a mouse button has been released on a component
   void mouseClicked(MouseEvent event);
       // Called when the mouse has been clicked on a component
   void mouseEntered(MouseEvent event);
       // Called when the mouse enters a component
   void mouseExited(MouseEvent event);
       // Called when the mouse exits a component
}

The mousePressed and mouseReleased methods are called whenever a mouse button is pressed or released. If a button is pressed and released in quick succession, and the mouse has not moved, then the mouseClicked method is called as well. The mouseEntered and mouseExited methods can be used to paint a user-interface component in a special way whenever the mouse is pointing inside it.

The most commonly used method is mousePressed. Users generally expect that their actions are processed as soon as the mouse button is pressed.

You add a mouse listener to a component by calling the addMouseListener method:

public class MyMouseListener implements MouseListener
{
   // Implements five methods
}

MouseListener listener = new MyMouseListener();
component.addMouseListener(listener);

In our sample program, a user clicks on a component containing a rectangle. Whenever the mouse button is pressed, the rectangle is moved to the mouse location. We first enhance the RectangleComponent class and add a moveTo method to move the rectangle to a new position.

ch09/mouse/RectangleComponent.java

1   import java.awt.Graphics;
2   import java.awt.Graphics2D;
3   import java.awt.Rectangle;
4   import javax.swing.JComponent;
5
6 /**
7    This component displays a rectangle that can be moved.
8 */
9 public class RectangleComponent extends JComponent
10 {
11    private static final int BOX_X = 100;
12    private static final int BOX_Y = 100;
13    private static final int BOX_WIDTH = 20;
14    private static final int BOX_HEIGHT = 30;
15
16    private Rectangle box;
17
18    public RectangleComponent()
19    {
20       // The rectangle that the paint method draws
21       box = new Rectangle(BOX_X, BOX_Y, BOX_WIDTH, BOX_HEIGHT);
22    }
23
24    public void paintComponent(Graphics g)
25    {
26       Graphics2D g2 = (Graphics2D) g;
27
28       g2.draw(box);
29    }
30
31    /**
32       Moves the rectangle to the given location.
33       @param x the x-position of the new location
34       @param y the y-position of the new location
35   */
36    public void moveTo(int x, int y)
37    {
38       box.setLocation(x, y);
39       repaint();
40    }
41 }

Note the call to repaint in the moveTo method. As explained in the preceding section, this call causes the component to repaint itself and show the rectangle in the new position.

Now, add a mouse listener to the component. Whenever the mouse is pressed, the listener moves the rectangle to the mouse location.

class MousePressListener implements MouseListener
{
   public void mousePressed(MouseEvent event)
   {
      int x = event.getX();
      int y = event.getY();
      component.moveTo(x, y);
   }

   // Do-nothing methods
   public void mouseReleased(MouseEvent event) {}
   public void mouseClicked(MouseEvent event) {}
   public void mouseEntered(MouseEvent event) {}
   public void mouseExited(MouseEvent event) {}
}
Clicking the Mouse Moves the Rectangle

Figure 9.8. Clicking the Mouse Moves the Rectangle

It often happens that a particular listener specifies actions only for one or two of the listener methods. Nevertheless, all five methods of the interface must be implemented. The unused methods are simply implemented as do-nothing methods.

Go ahead and run the RectangleComponentViewer program. Whenever you click the mouse inside the frame, the top-left corner of the rectangle moves to the mouse pointer (see Figure 8).

ch09/mouse/RectangleComponentViewer.java

1 import java.awt.event.MouseListener;
2 import java.awt.event.MouseEvent;
3 import javax.swing.JFrame;
4
5 /**
6    This program displays a RectangleComponent.
7 */
8 public class RectangleComponentViewer
9 {
10   private static final int FRAME_WIDTH = 300;
11   private static final int FRAME_HEIGHT = 400;
12
13   public static void main(String[] args)
14   {
15      final RectangleComponent component = new RectangleComponent();
16
17      // Add mouse press listener
18
19      class MousePressListener implements MouseListener
20      {
21         public void mousePressed(MouseEvent event)
22         {
23            int x = event.getX();
24            int y = event.getY();
25            component.moveTo(x, y);
26         }
27
28          // Do-nothing methods
29          public void mouseReleased(MouseEvent event) {}
30          public void mouseClicked(MouseEvent event) {}
31          public void mouseEntered(MouseEvent event) {}
32          public void mouseExited(MouseEvent event) {}
33       }
34
35       MouseListener listener = new MousePressListener();
36       component.addMouseListener(listener);
37
38       JFrame frame = new JFrame();
39       frame.add(component);
40
41       frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
42       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
43       frame.setVisible(true);
44    }
45 }
  1. Clicking the Mouse Moves the Rectangle
  2. Why must the MousePressListener class supply five methods?

Summary of Learning Objectives

Use interfaces for making a service available to multiple classes.

  • A Java interface type declares methods but does not provide their implementations.

  • Unlike a class, an interface type provides no implementation.

  • Use the implements reserved word to indicate that a class implements an interface type.

  • Use interface types to make code more reusable.

Describe how to convert between class and interface types.

  • You can convert from a class type to an interface type, provided the class implements the interface.

  • You need a cast to convert from an interface type to a class type.

Describe dynamic method lookup and polymorphism.

  • When the virtual machine calls an instance method, it locates the method of the implicit parameter's class. This is called dynamic method lookup.

  • Polymorphism denotes the ability to treat objects with differences in behavior in a uniform way.

Describe how to use interface types for providing callbacks.

  • A callback is a mechanism for specifying code that is executed at a later time.

Use inner classes to limit the scope of a utility class.

  • An inner class is declared inside another class.

  • Inner classes are commonly used for utility classes that should not be visible elsewhere in a program.

Use mock objects for supplying test versions of classes.

  • A mock object provides the same services as another object, but in a simplified manner.

  • Both the mock class and the actual class implement the same interface.

Recognize the use of events and event listeners in user-interface programming.

  • User-interface events include key presses, mouse moves, button clicks, menu selections, and so on.

  • An event listener belongs to a class that is provided by the application programmer. Its methods describe the actions to be taken when an event occurs.

  • Event sources report on events. When an event occurs, the event source notifies all event listeners.

  • Use JButton components for buttons. Attach an ActionListener to each button.

Implement event listeners as inner classes.

  • Methods of an inner class can access local and instance variables from the surrounding scope.

  • Local variables that are accessed by an inner class method must be declared as final.

Build graphical applications that use buttons.

  • Use a JPanel container to group multiple user-interface components together.

  • You specify button click actions through classes that implement the ActionListener interface.

Use a timer for drawing animations.

  • A timer generates timer events at fixed intervals.

  • The repaint method causes a component to repaint itself. Call repaint whenever you modify the shapes that the paintComponent method draws.

Write programs that process mouse events.

  • You use a mouse listener to capture mouse events.

Classes, Objects, and Methods Introduced in this Chapter

java.awt.Component                          java.awt.event.MouseListener
   addMouseListener                            mouseClicked
   repaint                                     mouseEntered
   setPreferredSize                            mouseExited
java.awt.Container                             mousePressed
   add                                         mouseReleased
java.awt.Dimension                          javax.swing.AbstractButton
java.awt.Rectangle                             addActionListener
   setLocation                              javax.swing.JButton
java.awt.event.ActionListener               javax.swing.JLabel
   actionPerformed                          javax.swing.JPanel
java.awt.event.MouseEvent                   javax.swing.Timer
   getX                                        start
   getY                                        stop

Media Resources

  • Worked Example Investigating Number Sequences

  • • Lab Exercises

  • Media Resources
  • Media Resources
  • Media Resources

Review Exercises

R9.1 Suppose C is a class that implements the interfaces I and J. Which of the following assignments require a cast?

C c = ...;
I i = ...;
J j = ...;
  1. c = i;

  2. j = c;

  3. i = j;

R9.2 Suppose C is a class that implements the interfaces I and J, and suppose i is declared as

I i = new C();

Which of the following statements will throw an exception?

  1. C c = (C) i;

  2. J j = (J) i;

  3. i = (I) null;

R9.3 Suppose the class Sandwich implements the Edible interface, and you are given the variable declarations

Sandwich sub = new Sandwich();
Rectangle cerealBox = new Rectangle(5, 10, 20, 30);
Edible e = null;

Which of the following assignment statements are legal?

  1. e = sub;

  2. sub = e;

  3. sub = (Sandwich) e;

  4. sub = (Sandwich) cerealBox;

  5. e = cerealBox;

  6. e = (Edible) cerealBox;

  7. e = (Rectangle) cerealBox;

  8. e = (Rectangle) null;

R9.4 How does a cast such as (BankAccount) x differ from a cast of number values such as (int) x?

R9.5 The classes Rectangle2D.Double, Ellipse2D.Double, and Line2D.Double implement the Shape interface. The Graphics2D class depends on the Shape interface but not on the rectangle, ellipse, and line classes. Draw a UML diagram denoting these facts.

R9.6 Suppose r contains a reference to a new Rectangle(5, 10, 20, 30). Which of the following assignments is legal? (Look inside the API documentation to check which interfaces the Rectangle class implements.)

  1. Rectangle a = r;

  2. Shape b = r;

  3. String c = r;

  4. ActionListener d = r;

  5. Measurable e = r;

  6. Serializable f = r;

  7. Object g = r;

R9.7 Classes such as Rectangle2D.Double, Ellipse2D.Double and Line2D.Double implement the Shape interface. The Shape interface has a method

Rectangle getBounds()

that returns a rectangle completely enclosing the shape. Consider the method call:

Shape s = ...;
Rectangle r = s.getBounds();

Explain why this is an example of polymorphism.

R9.8 In Java, a method call such as x.f() uses dynamic method lookup—the exact method to be called depends on the type of the object to which x refers. Give two kinds of method calls that do not look dynamic method lookup in Java.

R9.9 Suppose you need to process an array of employees to find the average and the highest salaries. Discuss what you need to do to use the implementation of the DataSet class in Section 9.1 (which processes Measurable objects). What do you need to do to use the second implementation (in Section 9.4)? Which is easier?

R9.10 What happens if you add a String object to the implementation of the DataSet class in Section 9.1? What happens if you add a String object to a DataSet object of the implementation in Section 9.4 that uses a RectangleMeasurer class?

R9.11 How would you reorganize the DataSetTester3 program if you needed to make RectangleMeasurer into a top-level class (that is, not an inner class)?

R9.12 What is a callback? Can you think of another use for a callback for the DataSet class? (Hint: Exercise P9.12.)

R9.13 Consider this top-level and inner class. Which variables can the f method access?

public class T
{
   private int t;

   public void m(final int x, int y)
   {
      int a;
      final int b;

      class C implements I
      {
         public void f()
         {
             ...
}
      }

      final int c;
      ...
   }
}

R9.14 What happens when an inner class tries to access a non-final local variable? Try it out and explain your findings.

R9.15 How would you reorganize the InvestmentViewer1 program if you needed to make AddInterestListener into a top-level class (that is, not an inner class)?

R9.16 What is an event object? An event source? An event listener?

R9.17 From a programmer's perspective, what is the most important difference between the user interfaces of a console application and a graphical application?

R9.18 What is the difference between an ActionEvent and a MouseEvent?

R9.19 Why does the ActionListener interface have only one method, whereas the MouseListener has five methods?

R9.20 Can a class be an event source for multiple event types? If so, give an example.

R9.21 What information does an action event object carry? What additional information does a mouse event object carry?

R9.22 Why are we using inner classes for event listeners? If Java did not have inner classes, could we still implement event listeners? How?

R9.23 What is the difference between the paintComponent and repaint methods?

R9.24 What is the difference between a frame and a panel?

Programming Exercises

P9.1 Have the Die class of Chapter 6 implement the Measurable interface. Generate dice, cast them, and add them to the implementation of the DataSet class in Section 9.1. Display the average.

P9.2 Implement a class Quiz that implements the Measurable interface. A quiz has a score and a letter grade (such as B+). Use the implementation of the DataSet class in Section 9.1 to process a collection of quizzes. Display the average score and the quiz with the highest score (both letter grade and score).

P9.3 A person has a name and a height in centimeters. Use the implementation of the DataSet class in Section 9.4 to process a collection of Person objects. Display the average height and the name of the tallest person.

P9.4 Modify the implementation of the DataSet class in Section 9.1 (the one processing Measurable objects) to also compute the minimum data element.

P9.5 Modify the implementation of the DataSet class in Section 9.4 (the one using a Measurer object) to also compute the minimum data element.

P9.6 Using a different Measurer object, process a set of Rectangle objects to find the rectangle with the largest perimeter.

P9.7 Enhance the DataSet class so that it can either be used with a Measurer object or for processing Measurable objects. Hint: Supply a constructor with no parameters that implements a Measurer that processes Measurable objects.

P9.8 Modify the display method of the LastDigitDistribution class of Worked Example 9.1 so that it produces a histogram, like this:

0: *************
1: ******************
2: *************

Scale the bars so that widest one has length 40.

P9.9 Write a class PrimeSequence that implements the Sequence interface of Worked Example 9.1, producing the sequence of prime numbers.

P9.10 Add a method hasNext to the Sequence interface of Worked Example 9.1 that returns false if the sequence has no more values. Implement a class MySequence producing a sequence of real data of your choice, such as populations of cities or countries, temperatures, or stock prices. Obtain the data from the Internet and reformat the values so that they are placed into an array. Return one value at a time in the next method, until you reach the end of the data. Your SequenceTester class should display all data in the sequence and check whether the last digits are randomly distributed.

P9.11 Provide a class FirstDigitDistribution that works just like the LastDigitDistribution class of Worked Example 9.1, except that it counts the distribution of the first digit of each value. (It is a well-known fact that the first digits of random values are not uniformly distributed. This fact has been used to detect accounting fraud, when sequences of transaction amounts had an unnatural distribution of their first digits.)

P9.12 Declare an interface Filter as follows:

public interface Filter
{
   boolean accept(Object x);
}

Modify the implementation of the DataSet class in Section 9.4 to use both a Measurer and a Filter object. Only objects that the filter accepts should be processed. Demonstrate your modification by having a data set process a collection of bank accounts, filtering out all accounts with balances less than $1,000.

P9.13 The standard Java library provides a Comparable interface:

public interface Comparable
{
   /**
      Compares this object with another.
      @param other the object to be compared
      @return a negative integer, zero, or a positive integer if this object
      is less than, equal to, or greater than, other
   */
   public int compareTo(Object other);
}

Modify the DataSet class of Section 9.1 to accept Comparable objects. With this interface, it is no longer meaningful to compute the average. The DataSet class should record the minimum and maximum data values. Test your modified DataSet class by adding a number of String objects. (The String class implements the Comparable inter-face.)

P9.14 Modify the Coin class to have it implement the Comparable interface described in Exercise P9.13.

P9.15 The System.out.printf method has predefined formats for printing integers, floating-point numbers, and other data types. But it is also extensible. If you use the S format, you can print any class that implements the Formattable interface. That interface has a single method:

void formatTo(Formatter formatter, int flags, int width, int precision)

In this exercise, you should make the BankAccount class implement the Formattable interface. Ignore the flags and precision and simply format the bank balance, using the given width. In order to achieve this task, you need to get an Appendable reference like this:

Appendable a = formatter.out();

Appendable is another interface with a method

void append(CharSequence sequence)

CharSequence is yet another interface that is implemented by (among others) the String class. Construct a string by first converting the bank balance into a string and then padding it with spaces so that it has the desired width. Pass that string to the append method.

P9.16 Enhance the formatTo method of Exercise P9.15 by taking into account the precision.

P9.17 Consider the task of writing a program that plays TicTacToe against a human opponent. A user interface TicTacToeUI reads the user's moves and displays the computer's moves and the board. A class TicTacToeStrategy determines the next move that the computer makes. A class TicTacToeBoard represents the current state of the board. Complete all classes except for the strategy class. Instead, use a mock class that simply picks the first available empty square.

P9.18 Consider the task of translating a plain text book from Project Gutenberg (http://gutenberg.org) to HTML. For example, here is the start of the first chapter of Tolstoy's Anna Karenina:

Chapter 1

Happy families are all alike; every unhappy family is unhappy in
its own way.

Everything was in confusion in the Oblonskys' house. The wife
had discovered that the husband was carrying on an intrigue with
a French girl, who had been a governess in their family, and she
had announced to her husband that she could not go on living in
the same house with him ...

The equivalent HTML is:

<h1>Chapter 1</h1>
<p>Happy families are all alike; every unhappy family is unhappy in
its own way.</p>
<p>Everything was in confusion in the Oblonskys' house. The wife
had discovered that the husband was carrying on an intrigue with
a French girl, who had been a governess in their family, and she
had announced to her husband that she could not go on living in
the same house with him ...</p>

The HTML conversion can be carried out in two steps. First, the plain text is assembled into segments, blocks of text of the same kind (heading, paragraph, and so on). Then each segment is converted, by surrounding it with the HTML tags and converting special characters.

Plain Text

HTML

" "

&ldquo; (left) or &rdquo; (right)

' '

&lsquo; (left) or &rsquo; (right)

&emdash;

<

&lt;

>

&gt;

&

&amp;

Fetching the text from the Internet and breaking it into segments is a challenging task. Provide an interface and a mock implementation. Combine it with a class that uses the mock implementation to finish the formatting task.

P9.19 Write a method randomShape that randomly generates objects implementing the Shape interface: some mixture of rectangles, ellipses, and lines, with random positions. Call it 10 times and draw all of them.

P9.20 Enhance the ButtonViewer program so that it prints a message "I was clicked n times!" whenever the button is clicked. The value n should be incremented with each click.

P9.21 Enhance the ButtonViewer program so that it has two buttons, each of which prints a message "I was clicked n times!" whenever the button is clicked. Each button should have a separate click count.

P9.22 Enhance the ButtonViewer program so that it has two buttons labeled A and B, each of which prints a message "Button x was clicked!", where x is A or B.

P9.23 Implement a ButtonViewer program as in Exercise P9.22, using only a single listener class.

P9.24 Enhance the ButtonViewer program so that it prints the time at which the button was clicked.

P9.25 Implement the AddInterestListener in the InvestmentViewer1 program as a regular class (that is, not an inner class). Hint: Store a reference to the bank account. Add a constructor to the listener class that sets the reference.

P9.26 Implement the AddInterestListener in the InvestmentViewer2 program as a regular class (that is, not an inner class). Hint: Store references to the bank account and the label in the listener. Add a constructor to the listener class that sets the references.

P9.27 Write a program that demonstrates the growth of a roach population. Start with two roaches and double the number of roaches with each button click.

P9.28 Write a program that uses a timer to print the current time once a second. Hint: The following code prints the current time:

Date now = new Date();
System.out.println(now);

The Date class is in the java.util package.

P9.29 Change the RectangleComponent for the animation program in Section 9.10 so that the rectangle bounces off the edges of the component rather than simply moving outside.

P9.30 Write a program that animates a car so that it moves across a frame.

P9.31 Write a program that animates two cars moving across a frame in opposite directions (but at different heights so that they don't collide.)

P9.32 Change the RectangleComponent for the mouse listener program in Section 9.11 so that a new rectangle is added to the component whenever the mouse is clicked. Hint: Keep an ArrayList<Rectangle> and draw all rectangles in the paintComponent method.

P9.33 Write a program that prompts the user to enter the x- and y-positions of the center and a radius, using JOptionPane dialogs.When the user clicks a "Draw" button, draw a circle with that center and radius in a component.

P9.34 Write a program that allows the user to specify a circle by typing the radius in a JOptionPane and then clicking on the center. Note that you don't need a "Draw" button.

P9.35 Write a program that allows the user to specify a circle with two mouse presses, the first one on the center and the second on a point on the periphery. Hint: In the mouse press handler, you must keep track of whether you already received the center point in a previous mouse press.

Programming Projects

Project 9.1 Design an interface MoveableShape that can be used as a generic mechanism for animating a shape. A moveable shape must have two methods: move and draw. Write a generic AnimationPanel that paints and moves any MoveableShape (or array list of Move-ableShape objects if you covered Chapter 7). Supply moveable rectangle and car shapes.

Project 9.2 Your task is to design a general program for managing board games with two players. Your program should be flexible enough to handle games such as tic-tac-toe, chess, or the Game of Nim of Project 6.2.

Design an interface Game that describes a board game. Think about what your program needs to do. It asks the first player to input a move—a string in a game-specific format, such as Be3 in chess. Your program knows nothing about specific games, so the Game interface must have a method such as

boolean isValidMove(String move)

Once the move is found to be valid, it needs to be executed—the interface needs another method executeMove. Next, your program needs to check whether the game is over. If not, the other player's move is processed. You should also provide some mechanism for displaying the current state of the board.

Design the Game interface and provide two implementations of your choice—such as Nim and Chess (or TicTacToe if you are less ambitious). Your GamePlayer class should manage a Game reference without knowing which game is played, and process the moves from both players. Supply two programs that differ only in the initialization of the Game reference.

Answers to Self-Check Questions

  1. It must implement the Measurable interface, and its getMeasure method must return the population.

  2. The Object class doesn't have a getMeasure method, and the add method invokes the getMeasure method.

  3. Only if x actually refers to a BankAccount object.

  4. No—a Coin reference can be converted to a Measurable reference, but if you attempt to cast that reference to a BankAccount, an exception occurs.

  5. Measurable is an interface. Interfaces have no instance variables and no method implementations.

  6. That variable never refers to a Measurable object. It refers to an object of some class—a class that implements the Measurable interface.

  7. The code fragment prints 500.05. Each call to add results in a call x.getMeasure(). In the first call, x is a BankAccount. In the second call, x is a Coin. A different getMeasure method is called in each case. The first call returns the account balance, the second one the coin value.

  8. The String class doesn't implement the Measurable interface.

  9. Implement a class StringMeasurer that implements the Measurer interface.

  10. A measurer measures an object, whereas getMeasure measures "itself", that is, the implicit parameter.

  11. Inner classes are convenient for insignificant classes. Also, their methods can access local and instance variables from the surrounding scope.

  12. Four: one for the outer class, one for the inner class, and two for the DataSet and Measurer classes.

  13. You want to implement the GradingProgram class in terms of that interface so that it doesn't have to change when you switch between the mock class and the actual class.

  14. Because the developer of GradingProgram doesn't have to wait for the GradeBook class to be complete.

  15. The button object is the event source. The listener object is the event listener.

  16. The ClickListener class implements the ActionListener interface.

  17. Direct access is simpler than the alternative—passing the variable as a parameter to a constructor or method.

  18. The local variable must be declared as final.

  19. First add label to the panel, then add button.

  20. The actionPerformed method does not access that variable.

  21. The timer needs to call some method whenever the time interval expires. It calls the actionPerformed method of the listener object.

  22. The moved rectangles won't be painted, and the rectangle will appear to be stationary until the frame is repainted for an external reason.

  23. Because you know the current mouse position, not the amount by which the mouse has moved.

  24. It implements the MouseListener interface, which has five methods.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.129.90