Translate

Saturday, 24 September 2016

Chapter 6 -- Interfaces

In this chapter you'll learn how to use Java interfaces to provide a common set of methods by which a group of classes can be accessed and to implement features of multiple inheritance. You'll cover the use of interface constants and learn how to declare objects using interface types. You will also learn how to extend and combine interfaces. When you finish this chapter, you'll be able to use interfaces with your Java classes.

The Purpose of Java Interfaces

The Java interface construct is borrowed from the Objective-C protocol. It is used to identify a common set of methods for the group of classes that implement the interface. It is also used to share constants between classes. Interfaces are used to provide the benefits of multiple inheritance without its implementation difficulties. They allow several classes to share a standard set of methods and constants without requiring these methods and constants to be implemented by a common superclass. Interfaces provide a standard framework for accessing classes. They are analogous to the interfaces that we encounter in everyday life. Any large class of real-world objects that you regularly manipulate usually has a standard interface. Radios and televisions provide a common set of controls for tuning channels and adjusting audio volume. Cars come with a standard interface for steering, throttling, and braking. Automated bank tellers provide the same general interface for performing bank transactions. To realize the potential use of Java interfaces, consider the diversity of objects that are manipulated by GUI-building programs. Such programs provide the capability to generate graphical user interfaces by clicking on interface controls and dragging them to appropriate places on the windows and dialog boxes being developed. The objects implementing these controls may support many different sets of methods. For example, one subset of the controls may be required to support cut, copy, and paste operations. These methods might be grouped into an EditObject interface. Another subset of the interface controls may be required to support click and double-click operations. These objects might implement a Clickable interface. Another subset may support drag-and-drop operations and implement the Draggable interface. Other groups of objects may implement multiple interfaces. For example, there might be objects that are both Clickable and Draggable.

The Benefits of Interfaces

Interfaces provide many advantages to the Java programmer. One is that they allow standard sets of methods to be used across the class hierarchy. For example, you can define the Editable interface to support cut, copy, and paste operations. The Editable interface can then be implemented by relevant classes and establish a uniform approach to implementing these common operations. Interface types allow objects to be referenced by the methods they support without considering their location in the class hierarchy. They make maximal use of dynamic binding, allowing objects to be accessed independently of their implementation details. For example, parameters can be defined as interface types and used by methods. These methods can invoke the interface methods of their arguments without having to determine the classes to which the arguments belong. Interfaces also support selective multiple inheritance. They allow various subsets of the features supported by different classes to be shared without mandating that all features of these classes be uniformly imposed as the result of inheritance. Finally, because interfaces are declared independently of classes, they are unaffected by changes to specific classes or to the class hierarchy as a whole.

Declaring Interfaces

Interfaces, like classes, are not objects, but type definitions that can be used to declare an object. Interfaces are declared as follows:
InterfaceModifiers interface InterfaceName ExtendsClause InterfaceBody
The allowed interface modifiers are abstract and public. By default, all interfaces are abstract. The public access specifier allows interfaces to be accessed outside of the package in which they are declared, in the same manner as with classes. Only one class or interface may be declared public in a given compilation unit. The compilation unit must have the same name as its public interface or class. The extends clause is similar to the class extends clause and is covered later in this chapter. The body of an interface begins with an opening brace ({), consists of zero or more variable or method declarations, and ends with a closing brace (}). All variables declared in an interface body are assumed to be both static and final, must have a constant initializer, and are implemented as constant class variables. All methods declared in an interface body are assumed to be abstract and do not have method bodies. Only access methods can be declared within an interface; constructors are not allowed. The access specifier of a method is that of the interface in which it is declared. An example of a simple interface declaration is as follows:
public interface Clickable {
 void click();
 void doubleClick();
}
This Clickable interface is declared as public so that it can be accessed outside its package. It contains two method declarations, click() and doubleClick(). These methods must be supported by all classes that implement the Clickable interface.

Implementing Interfaces

The interfaces implemented by a class are identified in the implements clause of the class declaration. For example, the following class implements the Scrollable and Clickable interfaces:
class ExampleClass implements Scrollable, Clickable {
}
A non-abstract class must implement all interface methods that are not implemented by its superclasses. abstract classes are not required to implement interface methods. They can defer interface implementation to their non-abstract subclasses.

The CDrawApp Interface Example

To provide a concrete example of the use of interfaces, we'll extend the CDrawApp program, introduced in Chapter 5, "Classes and Objects," to include support for editable objects. Editable objects are objects that display text on the grid and can be edited using the CGTextEdit interface. The CGText class will be modified to support this interface. The CGPoint and CGBox classes will be extended by the subclasses, CGTextPoint and CGTextBox, both of which provide the capability to display text on the grid. The CGText, CGTextPoint, and CGTextBox classes will implement the CGTextEdit interface. Figure 6.1 shows the extensions to the Chapter 5 class hierarchy that were made to support this example. Figure 6.1 : Extensions to the CDrawApp class hierarchy. Before going on to edit and compile the source code files for this example, be sure to create a ch06 subdirectory under c:\java\jdg. This subdirectory should be used to store all the source Java files that you develop in this chapter.

The CGTextEdit Interface

The CGTextEdit interface is quite simple. Its source code is shown in Listing 6.1.
Listing 6.1. The CGTextEdit interface source code. package jdg.ch06;

public interface CGTextEdit {
 public void replaceText(String s);
 public void upperCase();
 public void lowerCase();
}

The CGTextEdit interface source code declares three methods: replaceText(), upperCase(), and lowerCase(). These methods must be provided by all classes that implement the CGTextEdit interface. The replaceString() method is used to replace the text associated with an object with the text contained in the String's parameter. The upperCase() and lowerCase() methods are used to convert the text associated with an object to upper- and lowercase, respectively. CGTextEdit and all its interfaces are declared as public, allowing them to be accessed outside of the jdg.ch06 package. The public modifiers used with the method declarations are redundant. Any method declared in a public interface is public, by default. After you have entered the CGTextEdit interface into the file, CGTextEdit.java, use javac to compile CGTextEdit.java. Do this from within the ch06 directory.

Updating the CGText Class

The CGText class, developed in Chapter 5, will be updated to implement the CGTextEdit interface. The easiest way to do this is to copy CGText.java from the c:\java\jdg\ch05 directory to the c:\java\jdg\ch06 directory and then edit it. Its source code is shown in Listing 6.2.
Listing 6.2. The CGText class source code. package jdg.ch06;

import jdg.ch05.CGObject;
import jdg.ch05.Point;
import jdg.ch05.PrintCGrid;
import java.lang.System;

// CGText.java

public class CGText extends CGObject implements CGTextEdit {
 // Variable declarations
 String text;

 // Method declarations
 public CGText(Point p,String s) {
  location = p;
  drawCharacter = ' ';
  text = s;
 }
 public void display(PrintCGrid grid) {
  Point p = new Point(location);
  for(int i=0;i<text.length();++i){
   grid.putCharAt(text.charAt(i),p);
   p = p.add(1,0);
  }
 }
 public void describe() {
  System.out.println("CGText "+location.toString()+" "+text);
 }
 public void replaceText(String s) {
  text=s;
 }
 public void upperCase() {
  text = text.toUpperCase();
 }
 public void lowerCase() {
  text = text.toLowerCase();
 }
}

All you need to do is to change the package statement, add the import statements, edit the class declaration, and add the last three methods that implement the CGTextEdit interface. Because this class is contained in the jdg.ch06 package, you need to import the CGObject, Point, and PrintCGrid classes from the jdg.ch05 package. The class declaration is changed to add the implements clause with the CGTextEdit interface. The three new methods are all very simple. The replaceText() method assigns text to the new value passed by the s parameter. The upperCase() and lowerCase() methods use the toUpperCase() and toLowerCase() methods of the String class to perform their conversions. You should compile the new CGText.java before moving on to the next class.

The CGTextPoint Class

The CGTextPoint class extends the CGPoint class to add the capability to display text along with the character point. (See Listing 6.3.)
Listing 6.3. The CGTextPoint class source code. package jdg.ch06;

import jdg.ch05.Point;
import jdg.ch05.CGPoint;
import jdg.ch05.PrintCGrid;
import java.lang.System;

// CGTextPoint.java

public class CGTextPoint extends CGPoint implements CGTextEdit {
 // Variable declarations
 String text;

 // Method declarations
 public CGTextPoint(Point p,char ch,String s) {
  super(p,ch);
  text = s;
 }
 public CGTextPoint(Point p,String s) {
  super(p);
  text = s;
 }
 public CGTextPoint(Point p,char ch) {
  super(p,ch);
  text = "";
 }
 public CGTextPoint(Point p) {
  super(p);
  text = "";
 }
 public void display(PrintCGrid grid) {
  super.display(grid);
  Point p = location.add(1,0);
  for(int i=0;i<text.length();++i){
   grid.putCharAt(text.charAt(i),p);
   p = p.add(1,0);
  }
 }
 public void describe() {
  System.out.print("CGTextPoint "+String.valueOf(drawCharacter)+" ");
  System.out.println(location.toString()+" "+text);
 }
 public void replaceText(String s) {
  text=s;
 }
 public void upperCase() {
  text = text.toUpperCase();
 }
 public void lowerCase() {
  text = text.toLowerCase();
 }
}

CGTextPoint declares the variable text. This variable is used to store the text associated with the point. It provides four constructors, each of which uses the super() constructor call statement to invoke the constructors of the CGPoint class. The four constructors allow CGTextPoint to be constructed using different combinations of parameters. The display() method invokes the display() method of its superclass to display the point at its location on the grid. It then displays the value of the text variable to the immediate right of this point. The describe() method displays a description of the text point on the console window. The replaceText(), upperCase(), and lowerCase() methods are the same as those of the new CGText class.

The CGTextBox Class

The CGTextBox class extends the CGBox class to add the capability to display text within a box. (See Listing 6.4.) The size of the box is automatically fitted to the size of the text to be displayed.
Listing 6.4. The CGTextBox class source code. package jdg.ch06;

import jdg.ch05.Point;
import jdg.ch05.CGBox;
import jdg.ch05.PrintCGrid;
import java.lang.System;

// CGTextBox.java

public class CGTextBox extends CGBox implements CGTextEdit {
 // Variable declarations
 String text;

 // Method declarations
 public CGTextBox(Point ulCorner, char ch, String s) {
  super(ulCorner,ulCorner.add(s.length()+1,2),ch);
  text = s;
 }
 public CGTextBox(Point ulCorner, String s) {
  super(ulCorner,ulCorner.add(s.length()+1,2));
  text = s;
 }
 public void display(PrintCGrid grid) {
  super.display(grid);
  Point p = location.add(1,1);
  for(int i=0;i<text.length();++i){
   grid.putCharAt(text.charAt(i),p);
   p = p.add(1,0);
  }
 }
 public void describe() {
  System.out.print("CGTextBox "+String.valueOf(drawCharacter)+" ");
  System.out.println(location.toString()+" "+lr.toString()+" "+text);
 }
 public void replaceText(String s) {
  text=s;
  lr=location.add(text.length()+1,2);
 }
 public void upperCase() {
  text = text.toUpperCase();
 }
 public void lowerCase() {
  text = text.toLowerCase();
 }
}

The CGTextBox class source code defines the text variable in the same manner as the CGTextPoint class and provides two constructors for initializing objects of its class. Both constructors use calls to the CGBox class to support the initialization. The parameters to these calls calculate the lower-right corner of the box using the upper-left corner as a reference point and adding horizontal and vertical offsets that size the box based on the length of the text it contains. The display() method displays a box using the display() method of its parent. It then displays text within the box. The describe() method prints a box's parameters on the console window. The upperCase() and lowerCase() methods are the same as those of the CGTextPoint class, but the replaceText() method is different. It updates the lr variable to correctly resize the box based on changes to the length of the text variable.

Updating the CDraw Class

The CDraw class is updated to support the Edit Text command. This requires changes to all its access methods except the addText() method. The source code of the CDrawApp and CDraw classes is shown in Listing 6.5.
Listing 6.5. The CDrawApp and CDraw classes. package jdg.ch06;

import jdg.ch05.Point;
import jdg.ch05.CGrid;
import jdg.ch05.PrintCGrid;
import jdg.ch05.BorderedPrintCGrid;
import jdg.ch05.CGObject;
import jdg.ch05.CGPoint;
import jdg.ch05.CGBox;
import jdg.ch05.KeyboardInput;
import java.lang.System;
import java.lang.ClassCastException;
import java.io.IOException;

class CDrawApp {
 public static void main(String args[]) throws IOException {
  CDraw program = new CDraw();
  program.run();
 }
}

class CDraw {
 // Variable declarations
 static KeyboardInput kbd = new KeyboardInput(System.in);
 BorderedPrintCGrid grid;

 // Method declarations
 CDraw() {
  grid = new BorderedPrintCGrid();
 }
 void run() throws IOException {
  boolean finished = false;
  do {
   char command = getCommand();
   switch(command){
    case 'P':
     addPoint();
     System.out.println();
     break;
    case 'B':
     addBox();
     System.out.println();
     break;
    case 'T':
     addText();
     System.out.println();
     break;
    case 'U':
     grid.deleteLastObject();
     System.out.println();
     break;
    case 'C':
     grid.clearGrid();
     System.out.println();
     break;
    case 'S':
     grid.show();
     break;
   case 'E':
    editText();
    break;
   case 'X':
    finished = true;
    default:
     System.out.println();
   }
  } while (!finished);
 }
 char getCommand() throws IOException {
  System.out.print("CDrawApp P - Add a Point U - Undo Last Add");
  System.out.println(" E - Edit Text");
  System.out.print("Main Menu B - Add a Box C - Clear Grid");
  System.out.println(" X - Exit CDrawApp");
  System.out.print(" T - Add Text S - Show Grid");
  System.out.print(" Enter command: ");
  System.out.flush();
  return Character.toUpperCase(kbd.getChar());
 }
 void addPoint() throws IOException {
  System.out.println("Add Point Menu");
  System.out.println(" Location:");
  Point p = kbd.getPoint();
  System.out.print(" Character: ");
  System.out.flush();
  char ch = kbd.getChar();
  if(ch==' ') ch = '+';
  System.out.print(" Add text (y/n): ");
  System.out.flush();
  if('Y'==Character.toUpperCase(kbd.getChar())) {
   System.out.print(" Text: ");
   System.out.flush();
   String s = kbd.getText();
   CGTextPoint cgtp = new CGTextPoint(p,ch,s);
   cgtp.addToGrid(grid);
  }else{
   CGPoint cgp = new CGPoint(p,ch);
   cgp.addToGrid(grid);
  }
 }
 void addBox() throws IOException {
  System.out.println("Add Box Menu");
  System.out.println(" Upper Left Corner:");
  Point ul = kbd.getPoint();
  System.out.print(" Add text (y/n): ");
  System.out.flush();
  if('Y'==Character.toUpperCase(kbd.getChar())) {
   System.out.print(" Text: ");
   System.out.flush();
   String s = kbd.getText();
   System.out.print(" Character: ");
   System.out.flush();
   char ch = kbd.getChar();
   if(ch==' ') ch = '#';
   CGTextBox cgtb = new CGTextBox(ul,ch,s);
   cgtb.addToGrid(grid);
  }else{
   System.out.println(" Lower Right Corner:");
   Point lr = kbd.getPoint();
   System.out.print(" Character: ");
   System.out.flush();
   char ch = kbd.getChar();
   if(ch==' ') ch = '#';
   CGBox box = new CGBox(ul,lr,ch);
   box.addToGrid(grid);
  }
 }
 void addText() throws IOException {
  System.out.println("Add Text Menu");
  System.out.println(" Location:");
  Point p = kbd.getPoint();
  System.out.print(" Text: ");
  System.out.flush();
  String text = kbd.getText();
  CGText cgt = new CGText(p,text);
  cgt.addToGrid(grid);
 }
 void editText() throws IOException {
  System.out.println("Current Objects:");
  int numObjects = grid.getNumObjects();
  for(int i=0;i<numObjects;++i){
   System.out.print(" "+String.valueOf(i)+" ");
   grid.getObject(i).describe();
  }
  if(numObjects > 0){
   System.out.print("Select an object to edit: ");
   System.out.flush();
   int objIndex = kbd.getInt();
   CGObject obj = grid.getObject(objIndex);
   try {
    editText((CGTextEdit) obj);
   }catch (ClassCastException ex){
    System.out.println("Object is not editable.");
   }
  }else System.out.println("(none)");
  System.out.println();
 }
 void editText(CGTextEdit obj) throws IOException {
  System.out.println("Text Edit Menu");
  System.out.println(" R - Replace Text");
  System.out.println(" L - Lower Case");
  System.out.println(" U - Upper Case");
  System.out.print("Enter command: ");
  System.out.flush();
  char ch = kbd.getChar();
  ch = Character.toUpperCase(ch);
  switch(ch) {
   case 'R':
    System.out.print("Enter new text: ");
    System.out.flush();
    String s = kbd.getText();
    obj.replaceText(s);
    break;
   case 'L':
    obj.lowerCase();
    break;
   case 'U':
    obj.upperCase();
    break;
  }
 }
}

The run(), getCommand(), addPoint(), and addBox() methods are updated to support the Edit Text command. The two overloaded editText() methods are added to process this command. The switch statement of the run() method adds the 'E' case to its list of command options, calling the editText() method to process the Edit Text command. The getCommand() method adds the Edit Text command to its menu display. The addPoint() and addBox() methods query the user to determine whether text should be added to the point or box. If the user declines to add text, a CGPoint or CGBox object is created and added to the grid. If the user indicates that he or she wants to add text to the point or box, the user is prompted to enter the text. In this case, CGTextPoint and CGTextBox objects are created and added to the grid. The two editText() methods share the same name but provide completely different processing. The first editText() method is invoked when the user enters the Edit Text command. It displays a list of the objects that are currently added to the grid. It does this by using the getNumObjects() method of the PrintCGrid class to find out how many objects there are and then retrieving those objects using the getObject() method of the PrintCGrid class. The following line of code concatenates two method invocations:
grid.getObject(i).describe();
It retrieves an object of class CGObject by invoking the getObject() method of the PrintCGrid class. It then invokes the object's describe() method so that it will display its description on the console window. If there are no objects currently added to the grid, the editText() method indicates this fact by displaying (none) to the console window. Otherwise, the user is prompted to enter the number of the object to edit. This number is the number listed in the current object display. The number entered by the user is used to retrieve the object to be edited using the getObject() method. After the object is retrieved, the editText() method tries to edit the text associated with the object by invoking the second editText() method. If the object does not implement the CGTextEdit interface, a ClassCastException is thrown during the invocation of the second editText() method. The first editText() method catches this exception and reports the selected object as not being editable. The second editText() method displays a Text Edit Menu prompt to the user and invokes the replaceText(), lowerCase(), and upperCase() methods to process the commands entered by the user.

Running the Example

The CDrawApp program is compiled and executed in the same manner as its Chapter 5 predecessor. You should notice the additional Edit Text command provided in the CDrawApp main menu:
C:\java\jdg\ch06>java jdg.ch06.CDrawApp
CDrawApp    P - Add a Point   U - Undo Last Add   E - Edit Text
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command:
We'll add a few objects to the grid, display them, and then edit their text. After you learn how to use the new program, we'll discuss its features as they relate to interfaces. Enter P to add a point to the grid. Set its x-coordinate to 60, its y-coordinate to 10, and its draw character to @:
Add Point Menu
 Location:
  x-coordinate: 60
  y-coordinate: 10
 Character: @
 Add text (y/n):
You are asked whether you want to add text to the point. Press Y to add text. You are then prompted to add your text. Enter at sign as your text, as shown in the following display output:
Add Point Menu
 Location:
  x-coordinate: 60
  y-coordinate: 10
 Character: @
 Add text (y/n): y
  Text: at sign
The CDrawApp main menu is then redisplayed. Enter B to add a box. Set the box's upper-left corner as follows:
Add Box Menu
 Upper Left Corner:
  x-coordinate: 4
  y-coordinate: 4
 Add text (y/n):
Enter Y to add text to the box. You are prompted to enter your text. Enter the text Java's interfaces support multiple inheritance.. Then set the box's draw character to +. Your display output should look like the following:
Add Box Menu
 Upper Left Corner:
  x-coordinate: 4
  y-coordinate: 4
 Add text (y/n): y
  Text: Java's interfaces support multiple inheritance.
 Character: +
Enter B to enter another box. This box will not contain any text. Enter the coordinates for the box's corners, as follows:
Add Box Menu
 Upper Left Corner:
  x-coordinate: 65
  y-coordinate: 15
 Add text (y/n): n
 Lower Right Corner:
  x-coordinate: 72
  y-coordinate: 18
Then set its draw character to a hyphen: Character: - You should have noticed that when a box contains text, its lower-right corner is not specified. That's because the program computes it based on the length of the text to be displayed with the box. You're almost done adding objects to the grid. Enter T to add text to the grid. Set the text's location and value as follows:
Add Text Menu
 Location:
  x-coordinate: 1
  y-coordinate: 18
 Text: UPPER CASE Or lower case
You now have enough objects to work with. Enter S to display the grid. It should look like this:
***************************************************************************
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*    +++++++++++++++++++++++++++++++++++++++++++++++++                      *
*    +Java's interfaces support multiple inheritance.+                      *
*    +++++++++++++++++++++++++++++++++++++++++++++++++                      *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                 @ at sign      *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                      --------  *
*                                          &nb sp;                      -      -  *
*                                          &nb sp;                      -      -  *
* UPPER CASE Or lower case                                  & nbsp;     --------  *
*                                          &nb sp;                                *
***************************************************************************
Let's start editing these objects. Enter E to select an object to edit:
CDrawApp    P - Add a Point   U - Undo Last Add   E - Edit Text
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command: e



Current Objects:
 0 CGTextPoint @ (60,10) at sign
 1 CGTextBox + (4,4) (52,6) Java's interfaces support multiple inheritance.
 2 CGBox - (65,15) (72,18)
 3 CGText (1,18) UPPER CASE Or lower case
Select an object to edit:


A list of the grid's current objects is displayed. Enter 2 to select the object of class CGBox. Because this object does not implement the CGTextEdit interface, it is identified as not being editable, as shown in the following console output:
Object is not editable.
See if you can find where this processing was performed within the CDraw class. Enter E again to edit another object. The list of current objects is again displayed. Enter 1 to select the object of class CGTextBox. The Text Edit Menu prompt is displayed as follows:
Text Edit Menu
 R - Replace Text
 L - Lower Case
 U - Upper Case
Enter command:
This menu allows you to use the methods of the CGTextEdit interface to edit the objects that implement the interface. Enter R to replace the text associated with the CGTextBox object. You are then prompted to enter the new text for this object. Enter interfaces to complete the editing. Your display should contain the following output:
Enter command: r
Enter new text: interfaces
Enter S to see how the grid was updated. Notice how the size of the CGTextBox was changed to fit the size of the text it contains:
++++++++++++
+interfaces+
++++++++++++
Enter E and then 0 to edit the object of class CGTextPoint. Then type U to change it to uppercase. Use the show command to verify that the text has been changed to uppercase. Enter E, 3, and L in succession to change the case of the text contained in the CGText object. Use the Show Grid command to redisplay the grid:
***************************************************************************
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*    ++++++++++++                                    &nb sp;                      *
*    +interfaces+                                    &nb sp;                      *
*    ++++++++++++                                    &nb sp;                      *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                 @ AT SIGN      *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                      --------  *
*                                          &nb sp;                      -      -  *
*                                          &nb sp;                      -      -  *
* upper case or lower case                                  & nbsp;     --------  *
*                                          &nb sp;                                *
***************************************************************************
Now type X to exit the CDrawApp program.

Example Summary

The CDrawApp program illustrates the use of a simple interface. The CGTextEdit interface, used in this example, provides a common set of access methods to three classes on different branches of the CDrawApp class hierarchy, as shown in Figure 6.2. Figure 6.2 : The interface. Providing a standard interface to the CGText, CGTextPoint, and CGTextBox classes makes it possible to manipulate objects of these classes as if they belong to a single type. This is illustrated in the second editText() method of the CDraw class. This method takes an object of the CGTextEdit interface type as an argument and edits that object without having to know to which class the object belongs. Although the methods of the CGTextEdit interface are very simple, the capability provided by the interface is not. The second editText() method is able to manipulate any object that implements the CGTextEdit interface, even those that may be defined in the future. The first editText() method of the CDraw class also provides an interesting example of the use of interfaces. The first part of the method displays the list of current objects, one of which is selected by the user. The try statement tests to see if the selected object implements the CGTextEdit interface by trying to cast it to an object of interface type CGTextEdit. The casting is performed when the object is used as an argument to the second editText() method:
try {
 editText((CGTextEdit) obj);
}catch (ClassCastException ex){
 System.out.println("Object is not editable.");
}
Casting is the process of changing the type by which an object is accessed. It is covered in Chapter 11, "Language Summary." An object can be cast to an interface type if the object implements the interface; otherwise, a ClassCastException is thrown. The editText() method uses casting to determine whether an object implements the CGTextEdit interface. If the object implements the CGTextEdit interface, the casting is allowed and the second editText() method is invoked to edit the object. If the object does not implement the CGTextEdit interface, an exception is thrown. This exception is caught by the catch part of the try statement and the object is identified as not editable.

Using Interfaces as Abstract Types

The preceding example shows how interfaces can be used to declare method parameters. Such declarations require only that the objects passed as arguments during a method invocation implement the identified interface. In the following example, the restart() method of ExampleClass declares the currentSelection parameter to be of type ExampleInterface. The restart() method has no idea what class of object is passed to it via currentSelection. It only requires it to implement the ExampleInterface. The fact that the correct methods are called for the object identified by currentSelection is an example of polymorphism. The code for ExampleInterface follows:
interface ExampleInterface {
    void rewind();
    void play();
    void stop();
}
class ExampleClass {
    void restart(ExampleInterface currentSelection) {
         currentSelection.stop();
         currentSelection.rewind();
         currentSelection.play();
    }
}
The restart() method uses the ExampleInterface methods of the currentSelection object to perform its processing. Note that ExampleClass does not need to implement ExampleInterface. The interface only needs to be implemented by the object passed via the currentSelection parameter.

Interface Constants

The variables declared in an interface body are available as constants to classes that implement the interface. This enables pools of related constants to be defined and made available to classes that require use of these constants. Common constant pools are color constants, mathematical constants, and formatting constants. Variables declared in an interface are implicitly static and final and must be initialized in their declaration. The following example shows how interface variables are used to define a constant pool. Create a file in your ch06 directory called ColorConstants.java and enter the code shown in Listing 6.6.
Listing 6.6. The ColorConstants interface defines red, blue, and green constants. package jdg.ch06;

public interface ColorConstants {
 int red = 0xff0000;
 int green = 0xff00;
 int blue = 0xff;

Compile this code using javac. Then create another file called ColorTestApp.java, as shown in Listing 6.7.
Listing 6.7. The ColorTestApp class source code. package jdg.ch06;

import java.lang.System;

class ColorTestApp implements ColorConstants {
 public static void main(String args[]) {
  System.out.println(red);
  System.out.println(green);
  System.out.println(blue);
 }
}

Compile ColorTestApp.java and execute it using java. Your console window should display the following output:
C:\java\jdg\ch06>java jdg.ch06.ColorTestApp
16711680
65280
255
The main() method of the ColorTestApp class is able to access the constants defined in the ColorConstants interface as if they were inherited variables. The main() function displays these hexadecimal constants as decimal integers.

Extending Interfaces

The interface extends clause is similar to the extends clause used with classes. An interface is allowed to extend more than one interface, but a class can extend only a single class. The extending interface inherits all the methods and constants defined in the interfaces that it extends. When a class implements an interface X, it must implement all methods of all interfaces that X extends. The following example declares the MoreTextEdit interface as extending the CGTextEdit interface. All classes that implement the MoreTextEdit interface must implement the methods of both CGTextEdit and MoreTextEdit:
interface MoreTextEdit extends CGTextEdit {
 void insertText(int pos, String s);
 void appendText(String s);
}

Combining Interfaces

Two or more interfaces may be combined into a single interface through extension. The following example shows how interfaces are combined:
interface TV {
 void changeChannel(int channelNumber);
 void changeVolume(int volumeSetting);
}
interface VCR {
 void play();
 void rewind();
 void record();
}
interface TVCR extends TV, VCR {
}
Any non-abstract class that implements the TVCR interface is required to implement the methods of the TV interface and the VCR interface.

Summary

In this chapter you have learned how to use Java interfaces to provide a common set of methods by which a group of classes can be accessed. You have added the CGTextEdit interface to the CDrawApp example from Chapter 5 and learned how to declare objects using interface types. You have also learned how to use interface constants. In Chapter 7, "Exceptions," you'll learn how to use Java exceptions to perform error processing.

Chapter 5--Classes and Objects

In this chapter you'll learn all about Java and object-oriented programming. You'll first cover general object-oriented programming concepts and then learn how to use Java classes to build object-oriented software. You'll use the information you learn to develop a sample Java program that illustrates the benefits of object-oriented programming.

Object-Oriented Programming Concepts

Over the many years since the dawn of computing, people have studied software-development approaches to figure out which approaches are quickest, cheapest, most reliable, and produce the best software. And over the years, many approaches and technologies have reigned as the best. As time goes on, we learn more about software development and are able to modify and adapt our approaches based on what we learn. The type of software we develop also changes over time as a result of improvements in computer hardware, innovations in computer science, and changes in user expectations. These improvements affect our development approaches as well. Of all the known approaches to developing software, one approach, called the object-oriented approach, has repeatedly proven itself to be the best approach for a large class of common software applications. It's likely that the object-oriented approach will undergo further evolution and that a new, improved software-development paradigm will take its place. But for right now, and the foreseeable future, it is recognized as the best approach for the majority of software that we develop today. Object-oriented programming focuses on the development of self-contained software components, called objects. These objects are modeled after things, such as files, forms, buttons, and windows, that appear in the real world. Objects are defined in terms of the information they contain and the operations they provide for using and manipulating this information.

It's an Object-Oriented World

This book is an object. It contains a lot of information. (If you don't believe me, try retyping it.) It also has methods for accessing the information it contains. For example, you can open the book, turn a page, read a paragraph, search the table of contents, and so on. The information contained in the book, together with the methods for accessing it, are what comprise the object known as this book. In order to read this book, you need some sort of light source. You could be reading it in the open sunshine or by moonlight, but let's assume that you are using a lamp of some kind. A lamp is also an object. It is an example of an object that contains information about its state. The state of an object is the particular condition it is in. For example, a lamp can be on or off. The lamp's methods-turn lamp on and turn lamp off-are used to access the state of the lamp. This book, too, has state information. For example, it can be open or closed. If it is open, it can be opened to a particular page. The pages are objects in their own right. They contain information and can be accessed through the read page method. The book object can be viewed as being composed of page objects. The book's methods provide access to pages, and the page methods provide access to the information contained on a particular page. The information contained in an object, whether it is state specific or not, is referred to as the object's data. The object's methods are said to access the data. Some methods return information about the object's data and are said to support read access. Other methods cause the data to be modified and are said to provide write access to the data. Finally, as you'll learn in later sections, some methods, called constructors, are used to create objects.

Composition and Reuse

The fact that one object can be composed of, or built from, other objects is the heart of object-oriented programming. This allows more complex objects to be constructed from simple object components. Just as you would not write a book as one continuous stream of text (unless you are Jack Kerouac), you wouldn't write a program as a single sequence of source code instructions. You design your program as an application object and construct it from other objects that are built or borrowed. For example, suppose you are developing a drawing program. Your drawing application would consist of objects such as windows, menus, a drawing canvas, a tool palette, a color palette, and so on. Some of these objects would be available in object libraries and others would be built from more primitive components. You would develop your drawing application by gathering and building its component objects and assembling them into an integrated whole. Object composition not only allows you to simplify the organization of your programs, it also lets you reuse the software you develop. For example, you could develop drawing objects as part of your drawing program and then reuse those objects in a paint program and a desktop- publishing program. You could also package up your drawing objects and give or sell them to others so that they can use them as a foundation for building their own custom objects. Object reuse provides you with the capability to build or acquire a library of objects from which you can more quickly and easily piece together your programs. Without this capability, you are forced to start from scratch with every program that you develop. Object reuse is not limited to object composition. It also exploits a powerful capability of object-oriented programming known as inheritance. Inheritance not only allows objects to be used as is, but also allows new objects to be created by extending and tailoring existing objects. Before you learn about inheritance, however, the concept of an object's class must be explained.

Classes

At this point, you might be wondering just how you go about developing objects. The answer, of course, depends on the language you are using. Java, C++, Smalltalk, and some other object-oriented languages follow a class-based approach. This approach allows you to declare classes that serve as a template from which objects are created. As you would expect, a class defines the type of data that is contained in an object and the methods that are used to access this data. A class also defines one or more methods to be used to create objects that are instances of the class. An instance of a class is a concrete manifestation of the class in your computer's memory. For example, consider a job application form as an object. It contains data-the different form fields that must be filled out. There are also methods for accessing the data-for example, fill in form and read form. Now suppose that you develop an application form for a company that will use it for new job applicants. When a job is advertised, 100 potential applicants show up. In order for these applicants to use your form, they must all be given a unique instance of the form. These form instances are created by using the form you developed as a master copy and then duplicating the master copy as many times as needed to create each instance. The job applicants then fill in their instances of the form, using the fill in form method. In the preceding example, the master form is analogous to a class. The master form defines the data to be contained in each of its instances and implicitly provides methods by which the data can be accessed. In the same way, a class defines the data that can be contained in an object as well as the methods that can be used to access this data.

Classification and Inheritance

Classification is a common way that we organize knowledge. When we encounter a new object in our daily experience, we try to fit that object in our hierarchical classification scheme. If it fits in an existing category, we know what kind of object it is. If it doesn't fit, we add a new category. Figure 5.1 describes how we use classification to represent knowledge. Figure 5.1. Hierarchical classification of knowledge. Figure 5.1 : Hierarchical classification of knowledge. When we classify objects in this hierarchical fashion, the object categories at the top of the classification tree include all the object categories below them. If an object category appears in the classification tree, it satisfies the properties of all object categories above it in the tree. Figure 5.2 presents a classification tree for vehicles. All categories in the tree below the category automobile, for example, share the common characteristics of being four-wheeled, self-powered, and designed for passenger transportation. Figure 5.2 : Vehicle classification tree. The fact that a lower-level category shares the characteristics of the categories above it on the classification tree is known as inheritance. The lower-level categories are said to inherit the characteristics of the categories above them on the tree. At this point, you're probably wondering what any of this has to do with object-oriented programming in general, and Java software development in particular. We're almost there. The classes you learned about in the previous section can also be organized in a hierarchical fashion. A class X is said to extend another class Y if it contains all the data contained in class Y and implements all the methods implemented by class Y. Class X is said to be a subclass of class Y, and class Y is said to be a superclass, or parent class, of class X. Classes form a hierarchical classification tree under the subclass relationship. If a class X is a subclass of a class Y, it inherits the properties of Y. This means that all of the data and methods defined for class Y are available to class X. Most object-oriented programming languages, and Java in particular, allow you to easily define subclasses that automatically inherit the data and methods of the classes they extend. This is a very powerful feature for software reuse. Not only can you reuse classes as they are defined, but you can easily extend and tailor their definitions by adding additional data and access methods to their subclasses. There are many times that you may have a class definition you can use in your program, but it would be better if it supported additional state information or access methods. Java's support of subclassing enables you to easily extend such classes by supplying only the additional data and methods that are unique to the subclass. This allows you to take advantage of all the features of the superclass without having to implement any of them.

Multiple Inheritance

When a class extends another class, it inherits the data and methods of the class it extends. This is known as single inheritance. It is also possible for a class to extend classes on more than one branch of the class hierarchy tree, as shown in Figure 5.3. This is known as multiple inheritance. Figure 5.3 : Multiple inheritance. Multiple inheritance poses some difficulties for object-oriented programming. Most of these difficulties stem from the problem of determining which parent of a class to use under certain conditions. Numerous ambiguities arise when a class may have more than one immediate parent. For example, suppose a class X extends both a class Y and a class Z. Both class Y and class Z implement a unique print method. How does the compiler determine what method should be used to print objects of class X? What if the ambiguity arises during runtime for an object that inherits methods over several widely spaced branches of the class hierarchy? What's a poor compiler to do? It is possible to design compilers and runtime systems that solve the ambiguities resulting from multiple inheritance, but these solutions tend to introduce a significant amount of processing overhead, adversely affecting program size and performance. The developers of Java have opted to support only single inheritance. This greatly simplifies the Java language, compiler, and runtime system. Java uses the interface construct to provide the benefits of multiple inheritance without the drawbacks resulting from parent ambiguity. You'll learn more about this construct in Chapter 6, "Interfaces."

Messages, Methods, and Object Interaction

In a pure object-oriented programming model, such as that used by Smalltalk, objects interact by sending messages to each other. When an object receives a message, the object invokes a method to process the message. The method may change the state of the object, return information contained in the object, or cause objects to be created or deleted. The object model used by Java is consistent with the concept of message passing, but does not emphasize it. In the Java model, objects interact by invoking each other's methods. Methods provide access to the information contained in an object. The type of access varies depending on the method.

Encapsulation

One of the characteristics of object-oriented programming that is often touted in discussions of the subject is encapsulation. The term carries the connotation of an object being enclosed in some sort of container-and that is exactly what it means. Encapsulation is the combining of data and the code that manipulates that data into a single component-that is, an object. Encapsulation also refers to the control of access to the details of an object's implementation. Object access is limited to a well-defined, controlled interface. This allows objects to be self-contained and protects them from accidental misuse, both of which are important to reliable software design.

Polymorphism

Polymorphism is the ability to assume different forms. In object-oriented programming, this refers to the ability of objects to have many methods of the same name, but with different forms. The compiler and runtime system support polymorphism by matching each method invocation to the correct method, class, and object. The ability to figure out which method to use, in complex situations, is the essence of polymorphism. Luckily for us, polymorphism is implemented in the compiler and runtime system-we don't need to do anything to make it happen. We just need to know that it works.

Dynamic Binding

Sometimes a program might need to interface with objects of many different classes. For example, consider a program that has the responsibility of sending out objects over a communication link. The program may not know what class an object belongs to until it is time to send it. The capability to defer until runtime decisions about what class an object belongs to and the methods for accessing the object is known as dynamic binding. Dynamic binding is important to object-oriented programming because it eliminates many potentially constraining assumptions about the classes that an object belongs to and enables objects to be designed in a more general and open manner. Dynamic binding also provides capabilities that are necessary for the advanced network programming capabilities of Java applets. When a browser executes a Java applet, the applet could require the loading of classes located on other sites across the Internet. Furthermore, these classes could be in a continual state of modification and upgrade. Dynamic binding allows new and modified objects to be used by executing software without requiring recompilation. The compiler and interpreter work together to provide executable code with the capabilities needed to dynamically interface with unknown objects during program execution.

Java Classes

In this section, you will develop a Java program, CDrawApp, that illustrates the concepts of encapsulation, composition and reuse, classification and inheritance, polymorphism, and dynamic binding. CDrawApp will allow you to draw points, boxes, and text strings on a character-based grid that is displayed using the Java console window. The program will be introduced as a series of Java classes that cover different aspects of class and object syntax. Each new class will make use of the capabilities provided by previous classes and will provide additional building blocks required for the program's development. It is important that you understand each class that is presented before moving on to subsequent classes. You should create a ch05 directory, under c:\java\jdg, to store the Java source and bytecode files for this lesson. All classes will be developed in the jdg.ch05 package.

Class Syntax

The Java language is class and object oriented. Classes are templates for the creation of objects. They define the data contained in an object together with methods for accessing that data. Classes are declared as follows:
ClassModifiers class ClassName ExtendsClause ImplementsClause ClassBody
The ClassModifiers, ExtendsClause, and ImplementsClause are optional. Interfaces and the ImplementsClause are covered in the next chapter. The ClassBody is enclosed by braces, and contains zero or more field declarations. An example of a simple class declaration is
class SimpleClass {
}
It declares a class, named SimpleClass, that is a subclass of Object, the highest-level class in the Java class hierarchy. SimpleClass declares no variables or methods of its own; it has only those that it inherits from Object.

The Point Class

The first class that you'll define for the CDrawApp program is the Point class. This class is used to identify a point on a grid by its x- and y-coordinates. The source code for the class declaration follows. (See Listing 5.1.) You should enter the code in a file named Point.java and store it in your c:\java\jdg\ch05 directory. Then compile it using the command javac Point.java.
Listing 5.1. The Point class source code.
package jdg.ch05;

// Point.java

public class Point {
 // Variable declarations
 private int x;
 private int y;

 //Method declarations
 public Point() {
  x = 0;
  y = 0;
 }
 public Point(int xValue, int yValue) {
  x = xValue;
  y = yValue;
 }
 public Point(Point p) {
  x = p.x();
  y = p.y();
 }
 public int x() {
  return x;
 }
 public int y() {
  return y;
 }
 public void xSet(int xValue) {
  x = xValue;
 }
 public void ySet(int yValue) {
  y = yValue;
 }
 public Point add(Point p) {
  return new Point(x+p.x(), y+p.y());
 }
 public Point add(int i,int j) {
  return new Point(x+i,y+j);
 }
 public String toString() {
  return new String("("+String.valueOf(x)+","+String.valueOf(y)+")");
 }
}

Class Modifiers

The Point class is declared using the public class modifier. Class modifiers are keywords that are used to specify the properties of a class. Three class modifiers are supported: public, final, and abstract. If a class is declared as public, it can be accessed outside of its package; otherwise, it cannot. Because Point is declared as public, it can be accessed outside its package. Only one public class or interface is allowed in a compilation unit. Point is the only class in Point.java and, therefore, follows this rule. If a class is declared as final, it cannot be extended. Final classes form the leaves of the class hierarchy tree. An abstract class is used to define the general behavior for an intended set of subclasses. abstract classes are used to set the stage for subsequent subclass development. They are, by definition, incomplete and cannot be instantiated in terms of objects. abstract classes describe the behavior expected of their subclasses through the declaration of abstract methods. abstract methods must be redefined, or overridden, before they can be used. Only abstract classes are allowed to declare or inherit abstract methods. The CGObject class is an example of an abstract class. It is presented in the section "The CGObject class," later in this chapter.

Extending Superclasses

When a class declaration does not contain an extends clause, the class is automatically made a subclass of the Object class. The Point class does not contain an extends clause and, therefore, is a subclass of Object. The Object class is at the top of the Java class hierarchy, being the superclass of all Java classes. You can change a class's position in the class hierarchy by identifying its immediate super-class in the class declaration. The immediate superclass is the parent class directly above it in the class hierarchy. You identify the immediate superclass of a class using the extends clause in the class declaration. For example, you can place SimpleClass under ExampleClass in the class hierarchy as follows:
class SimpleClass extends ExampleClass {
}
SimpleClass does not have any unique variables or methods of its own, but it inherits those of ExampleClass and all superclasses of ExampleClass.

Adding Body to Classes

The body of the Point class consists of all declarations between the opening and closing braces. If a class is to add any features to its superclass, it does so in its class body. It is here that additional variables and methods are declared. These additional declarations are referred to as field declarations. The field declarations are identified within the opening and closing braces ({ and }) of the class body. You need to supply the braces even if you don't intend to declare any fields, as you saw in the SimpleClass example.

Variable Declarations

Variables are the components of an object that store data and state information. They are declared as follows:
VariableModifiers Type VariableDeclarators
VariableModifiers are keywords that identify special properties of the variables being declared. Type is the Java type of the declared variables. It may be a primitive type, a class type, an interface type, or an array type. VariableDeclarators identify the names of the declared variables and can be used to specify the initial values of these variables. The Point class declares two integer variables: x and y. They are used to store the location of a point on a two-dimensional grid. These variables are declared with the private modifier. This modifier restricts access to the x and y variables to within the Point class. The Point class illustrates the principles of encapsulation. Data and methods are combined with a well-defined interface to provide maximum modularity. Access to the internal operation of the class is controlled.

Constructor Declarations

Constructors are special methods that are used to initialize newly created objects. They are used together with the new operator to create and initialize objects that are instances of a class. Constructors are declared in a slightly different manner than other methods. Their syntax is as follows:
AccessSpecifier ConstructorDeclarator ThrowsClause ConstructorBody
AccessSpecifier identifies the type of access allowed to the constructor. ConstructorDeclarator identifies a method with the same name as the class and specifies its parameter list. The parameter list is a comma-separated list of parameter declarations where each parameter declaration identifies the type and name of a parameter that is passed to the constructor upon invocation. ThrowsClause is a constructor. Exceptions are covered in Chapter 7, "Exceptions." ConstructorBody contains the code that implements the constructor. The Point class has three constructors:
 public Point() {
  x = 0;
  y = 0;
 }
 public Point(int xValue, int yValue) {
  x = xValue;
  y = yValue;
 }
 public Point(Point p) {
  x = p.x();
  y = p.y();
 }
All three constructors are identified as public. This allows them to be accessed outside of their package. The first constructor does not have any parameters. It simply initializes the x and y variables to 0. The second constructor has two parameters, xValue and yValue, of integer type. They are used to set the value of x and y. The third constructor takes an object of class Point as its parameter. It sets the values of x and y based upon the values of the parameter point p. It is an example of a copy constructor because it creates a new point that is a copy of the point that is passed as its parameter. The declaration of the three Point constructors is an example of overloading. Overloading occurs when two or more methods with the same name are declared within a class. The overloaded methods must differ in their parameter lists. The add() methods of the Point class are also overloaded. Overloading is an example of polymorphism. When an overloaded method is to be invoked during program execution, the number and type of method arguments used in the invocation determine which method is used.

Access Method Declarations

Methods are executable units of code that provide access to the data stored in variables. Methods that are not constructors are referred to as nonconstructor methods, or access methods. Access methods are declared within the body of a class as follows:
MethodModifiers ResultType MethodDeclarator ThrowsClause MethodBody
MethodModifiers identify special properties of a method. All the methods of class Point are public, allowing them to be accessed outside of their package. ResultType of a method identifies the type of value that is returned by the method. If an access method does not return a value, it must use the void return type. Constructors do not have a return type. The access methods of class Point have return values of type int, void, Point, and String. MethodDeclarator identifies the method, by name, and specifies its parameter list. The parameter list of access methods is specified in the same manner as with constructors. MethodBody contains the code that implements the Java method. The Point class has seven access methods. The x() and y() methods return the x- and y-coordinates of a point. The xSet() and ySet() methods set the values of these coordinates based on the values of the xValue and yValue parameters. The two add() methods are used to create a new Point object by adding to the coordinates of the point being accessed. The new operator creates new instances of a class. It is always followed by a constructor that initializes the newly created instance. The toString() method returns an object of class String that describes the point as an ordered pair.

The CGrid Class

The CGrid class is used to define a grid of characters of specified dimensions. It provides a basic set of grid methods and is extended by other classes that add to these methods. Its source code is shown in Listing 5.2. It should be entered into the CGrid.java file and compiled using javac.
Listing 5.2. The CGrid class source code.
package jdg.ch05;

// CGrid.java

public class CGrid {
 // Variable declarations
 protected int width;
 protected int depth;
 protected char grid[][];

 // Method declarations
 public CGrid(int widthValue,int depthValue) {
  width = widthValue;
  depth = depthValue;
  grid = new char[depth][width];
  blankGrid();
 }
 public void blankGrid() {
  fillGrid(' ');
 }
 public void fillGrid(char ch) {
  for(int j=0; j<depth; ++j)
   for(int i=0; i<width; ++i)
    grid[j][i]= ch;
 }
 public void putCharAt(char ch,Point p){
  grid[p.y()][p.x()] = ch;
 }
 public char getCharFrom(Point p) {
  return grid[p.y()][p.x()];
 }
}

CGrid declares three variables: width, depth, and grid[][]. The width and depth variables are used to specify the horizontal and vertical dimensions of grid[][], an array of character arrays that holds the characters of the grid. The grid[][] array is used as a two-dimensional array of characters, even though Java technically does not have multidimensional arrays. The CGrid variables are declared as protected. This specifies that they can only be accessed in the package, jdg.ch05, in which they are declared, and in any subclasses of CGrid. CGrid has a single constructor that sets the values of width and depth, allocates the grid[][] array, and then invokes blankGrid() to fill grid[][] with spaces. CGrid has four access methods. The fillGrid() method sets each element of the grid[][] array to the ch parameter. The blankGrid() method simply calls fillGrid() with a space character. The putCharAt() and getCharFrom() methods are used to set a point in the grid to a particular character and to find out what character is at a given location in the grid. Note that the putCharAt() and getCharFrom() methods use the Point class to define their parameters. Because Point is in the same package as CGrid, it does not need to be imported.

The CGObject Class

The CGObject class is an example of an abstract class. abstract classes are used to constrain the behavior of their subclasses by defining abstract methods. The abstract methods must be implemented by any non-abstract subclasses. Listing 5.3 shows the source code of the CGObject class.
Listing 5.3. The CGObject class source code. package jdg.ch05;

// CGObject.java

public abstract class CGObject {
 // Variable declarations
 public Point location;
 public char drawCharacter;

 // Method declarations
 public void addToGrid(PrintCGrid grid) {
  grid.addCGObject(this);
 }
 public abstract void display(PrintCGrid grid);
 public abstract void describe();
}

The CGObject class is used to define the general behavior of objects that may be displayed on a grid. It declares two variables: location and drawCharacter. The location variable is of type Point and is used to specify the point on a grid where an object is located. The drawCharacter variable identifies the character that should be used to draw the object. CGObject has three methods and no constructors. abstract classes cannot have constructors because they are incompletely defined and, therefore, cannot have object instances. The first method, addToGrid(), is not abstract. It takes an object of class PrintCGrid as a parameter and invokes the addCGObject() method of PrintCGrid to add this to the grid. The this keyword is used to refer to the current object. Whatever object of a subclass of CGObject that is invoked with the addToGrid() method is added to an object of class PrintCGrid. CGObject's other two methods are declared with the abstract keyword. This signifies that they must be overridden before they can be used by any non-abstract subclasses of CGObject. The overridden methods must have the same names, parameters, and return values as the abstract methods. The display() method will be used to display an object on a grid of class PrintCGrid. The describe() method will be used to display a description of a grid object. Don't forget to enter and compile CGObject before going on to the next class. In case you forgot, it should be entered into a file of the same name, with the .java extension-that is, CGObject.java.

The PrintCGrid Class

The PrintCGrid class is a subclass of the CGrid class. It defines additional variables and methods that allow objects to be added to a grid. It also provides methods for displaying the grid. The source code of the PrintCGrid class is shown in Listing 5.4.
Listing 5.4. The PrintCGrid class source code. package jdg.ch05;

import java.lang.System;

// PrintCGrid.java

public class PrintCGrid extends CGrid {
 // Variable declarations
 protected CGObject displayList[];
 protected static final int maxObjects = 100;
 protected int numObjects;

 // Method declarations
 public PrintCGrid(int x,int y) {
  super(x,y);
  numObjects = 0;
  displayList = new CGObject[maxObjects];
 }
 public void addCGObject(CGObject obj) {
  if(numObjects < maxObjects) {
   displayList[numObjects] = obj;
   ++numObjects;
  }
 }
 public void deleteCGObject(int index) {
  if(index < numObjects && numObjects > 0) {
   for(int i = index; i < numObjects -1 ; ++i)
    displayList[i] = displayList[i+1];
   --numObjects;
  }
 }
 public void deleteLastObject() {
   if(numObjects > 0) --numObjects;
 }
 public int getNumObjects() {
  return numObjects;
 }
 public CGObject getObject(int index) {
  return displayList[index];
 }
 public void clearGrid() {
  numObjects = 0;
 }
 public void drawGrid() {
  blankGrid();
  for(int i = 0; i < numObjects ; ++i)
   displayList[i].display(this);
 }
 public void displayGrid() {
  for(int i=0;i<depth;++i)
   System.out.println(String.valueOf(grid[i]));
 }
 public void displayRow(int row) {
  System.out.print(String.valueOf(grid[row]));
 }
 public void show() {
  drawGrid();
  displayGrid();
 }
}

PrintCGrid is identified as a subclass of CGrid by the extends clause in the PrintCGrid class declaration. This means that all the variables and methods defined in CGrid are available to PrintCGrid. You should now begin to get a feel for the power of inheritance. PrintCGrid uses CGrid as a base to which other grid display variables and methods are added. PrintCGrid declares three variables: displayList[], maxObjects, and numObjects. These variables are declared as protected, thereby limiting their access to the jdg.ch05 package and subclasses of PrintCGrid. The displayList[] variable is an array of class CGObject. This does not mean that it will contain objects that are instances of this class. That would be impossible, because CGObject is abstract. Declaring displayList[] to be an array of class CGObject allows it to hold objects of any class that is a subclass of CGObject. In general, if a variable is declared to be of class X, then the variable can be assigned any object of a class that is a subclass of X. The maxObjects variable is declared as both static and final. Variables that are declared using the static modifier are common to all objects that are instances of a class and are not replicated for each instance. Static variables are referred to as class variables. Variables that aren't declared as static are instance variables and are replicated for each object that is an instance of a class. The final modifier is used to identify a variable as a constant. A variable that is declared with the final modifier must be initialized in its declaration and cannot be assigned a value anywhere else outside its declaration. The maxObjects constant is initialized to 100. It is used to identify the maximum number of objects that can be added to displayList[]. The numObjects variable is used to count the actual number of objects that have been added to the grid's displayList[]. PrintCGrid has a single constructor. This constructor has two parameters, x and y, that represent the horizontal and vertical dimensions of the grid. The constructor invokes the super() method, passing these variables as arguments. The super() method is an example of a constructor call statement. It invokes the constructor of PrintCGrid's superclass, that is, CGrid, with the arguments x and y. CGrid's constructor initializes its width and depth variables, allocates the grid[][] array, and fills it with spaces. When CGrid's constructor is finished, PrintCGrid's constructor continues by setting numObjects to 0 and allocating the displayList[] array. PrintCGrid provides 10 access methods. The addCGObject() method adds an object to the displayList[] array. The deleteCGObject() method deletes the object at the specified index. All subsequent objects in the array are moved to fill the hole left by the deleted object. The deleteLastObject() method deletes the last object by merely decrementing numObjects. The getNumObjects() method returns the number of objects in displayList[]. The getObject() method returns the object at the specified position within displayList[]. The clearGrid() method clears all objects by setting numObjects to 0. The drawGrid() method is an interesting example of dynamic binding and the use of abstract classes. It blanks the grid, using the method that it inherits from CGrid, and then invokes the display() method of each object in displayList[]. It does not know what kind of objects are contained in displayList[]. It only knows that they are of some subclass of CGObject, and therefore must implement the display() method. Dynamic binding enables the display() method to be invoked for the correct object class. The displayGrid() method displays each row of the grid to the console window. It is an example of inheritance. The grid[][] array was defined in the CGrid class and inherited by PrintCGrid. It is updated by drawGrid() and the display() methods of all subclasses of CGObject. It is used by PrintCGrid to print characters to the console window. The valueOf() method used in displayGrid() is a static method of the String class. It converts an array of characters into a String object. A static method is similar to a static variable in that it applies to the class as a whole rather than to objects that are instances of the class. Because of this class orientation, a static method can access only static variables. All static methods are final. final methods cannot be overridden. The displayRow() method displays a single row of the grid to the console window and the show() method combines the drawGrid() and displayGrid() methods into a single method.

The BorderedPrintCGrid Class

The BorderedPrintCGrid class further extends the CGrid class by subclassing PrintCGrid. It adds additional variables and methods for creating a border around objects of class PrintCGrid. Listing 5.5 contains the source code of the BorderedPrintCGrid class.
Listing 5.5. The BorderedPrintCGrid class source code. package jdg.ch05;

// BorderedPrintCGrid.java

public class BorderedPrintCGrid extends PrintCGrid {
 // Variable declarations
 private boolean useBorder;
 private char borderCharacter;
 private String horizEdge;
 private String vertEdge;

 // Method declarations
 public BorderedPrintCGrid() {
  super(75,20);
  setBorderDefaults('*');
 }
 public BorderedPrintCGrid(int x,int y,char ch) {
  super(x,y);
  setBorderDefaults(ch);
 }
 private void setBorderDefaults(char ch) {
  useBorder = true;
  setBorderCharacter(ch);
 }
 public void enableBorder(boolean toggle) {
  useBorder = toggle;
 }
 public void setBorderCharacter(char ch) {
  borderCharacter = ch;
  char border[] = new char[width+2];
  for(int i=0;i<width+2;++i) border[i] = borderCharacter;
  horizEdge = new String(border);
  vertEdge = String.valueOf(borderCharacter);
 }
 public void displayGrid() {
  if(useBorder) {
   System.out.println(horizEdge);
   for(int i=0;i<depth;++i) {
    System.out.print(vertEdge);
    displayRow(i);
    System.out.println(vertEdge);
   }
   System.out.println(horizEdge);
  }else super.displayGrid();
 }
}

BorderedPrintCGrid declares four private variables: useBorder, borderCharacter, horizEdge, and vertEdge. The useBorder variable is of type boolean and determines whether a border should be displayed. The borderCharacter variable contains the character to be used to display the border. The horizEdge and vertEdge variables contain the String objects to be displayed for the horizontal and vertical edges of the border. These objects are computed from the borderEdge character based on the grid[][] dimensions. BorderedPrintCGrid has two constructors. The first does not take any parameters. It constructs a grid 75 characters wide and 20 rows high by calling the constructor of PrintCGrid. Note that PrintCGrid's constructor passes the call farther up the class hierarchy to CGrid's constructor. This is an example of how classification and inheritance work together to simplify the development of new classes and methods. The setBorderDefaults() method is used to initialize the variables of BorderedPrintCGrid. The second constructor is similar to the first, but provides the capability for the grid's dimensions to be specified directly. BorderedPrintCGrid provides four access methods. The setBorderDefaults() method initializes the variables of the BorderedPrintCGrid class using the enableBorder() and setBorderCharacter() methods. The enableBorder() method allows the useBorder variable to be set to true or false. The setBorderCharacter() method sets the borderCharacter, horizEdge, and vertEdge variables for use by the displayGrid() method. The displayGrid() method overrides the displayGrid() method of the PrintCGrid class. By doing so, it redefines the method to suit its own needs. It checks to see if the useBorder variable is true. If it is true, then a bordered grid is displayed using the displayRow() method of PrintCGrid. If it is false, it invokes the displayGrid() method of its superclass, PrintCGrid, to display the grid. The super keyword is used to identify the fact that a superclass method should be used instead of the one defined for the current class. The name of the superclass can also be used to indicate which method should be used. The method invocation could have used PrintCGrid.displayGrid() instead of super.displayGrid().

The CGPoint Class

The CGPoint class shows how a non-abstract class extends an abstract class. The CGPoint class extends CGObject. It does not add any new variables to those that it inherits, and the only methods that it declares are constructors and the abstract methods that it is required to implement. Listing 5.6 shows the source code of the CGPoint class.
Listing 5.6. The CGPoint class source code. package jdg.ch05;

// CGPoint.java

public class CGPoint extends CGObject {
 // Method declarations
 public CGPoint(int x, int y,char ch) {
  location = new Point(x,y);
  drawCharacter = ch;
 }
 public CGPoint(int x, int y) {
  this(x,y,'+');
 }
 public CGPoint(Point p) {
  this(p.x(),p.y(),'+');
 }
 public CGPoint(Point p,char ch) {
  this(p.x(),p.y(),ch);
 }
 public void display(PrintCGrid grid) {
  grid.putCharAt(drawCharacter,location);
 }
 public void describe() {
  System.out.print("CGPoint "+String.valueOf(drawCharacter)+" ");
  System.out.println(location.toString());
 }
}

CGPoint has four constructors. The first takes the x- and y-coordinates of a point and the character to be displayed, and initializes the location and drawCharacter variables declared in CGObject. The remaining constructors invoke the this() constructor. The this() constructor invokes a constructor for the current class that has a matching argument list. The matching constructor is the first constructor, in all three cases. The second, third, and fourth constructors provide a variety of parameter lists by which objects of CGPoint can be constructed. The second and third constructors supply default values for drawCharacter. The this() constructor call statement is similar to the super() constructor call statement used with the PrintCGrid and BorderedPrintCGrid classes. Each allows part of the construction details to be handed off to other constructors in the same and parent classes. If no constructor call statement is used, a default super() constructor is used. This ensures that when an object is created, constructors from all of its superclasses are called to initialize all variables inherited by the object. CGPoint overrides the display() and describe() abstract methods defined by CGObject. The display() method uses the putCharAt() method defined for class CGrid to draw a character in the grid[][] array. The describe() method prints a description of the point to the console window. It uses the toString() method of the Point class.

The CGBox Class

The CGBox class also extends CGObject. It provides an additional variable to allow a rectangle to be displayed on a grid. Listing 5.7 contains the CGBox class source code.
Listing 5.7. The CGBox class source code. package jdg.ch05;

// CGBox.java

public class CGBox extends CGObject {
 // Variable declarations
 protected Point lr; // Lower right corner of a box

 // Method declarations
 public CGBox(Point ulCorner, Point lrCorner,char ch) {
  location = ulCorner;
  lr = lrCorner;
  drawCharacter = ch;
 }
 public CGBox(Point ulCorner, Point lrCorner) {
  this(ulCorner,lrCorner,'#');
 }
 public void display(PrintCGrid grid) {
  int width = lr.x() - location.x() + 1;
  int height = lr.y() - location.y() + 1;
  Point topRow = new Point(location);
  Point bottomRow = new Point(location.x(),lr.y());
  for(int i=0; i<width; ++i) {
   grid.putCharAt(drawCharacter,topRow);
   grid.putCharAt(drawCharacter,bottomRow);
   topRow = topRow.add(1,0);
   bottomRow = bottomRow.add(1,0);
  }
  Point leftCol = new Point(location);
  Point rightCol = new Point(lr.x(),location.y());
  for(int i=0;i<height;++i){
   grid.putCharAt(drawCharacter,leftCol);
   grid.putCharAt(drawCharacter,rightCol);
   leftCol = leftCol.add(0,1);
   rightCol = rightCol.add(0,1);
  }
 }
 public void describe() {
  System.out.print("CGBox "+String.valueOf(drawCharacter)+" ");
  System.out.println(location.toString()+" "+lr.toString());
 }
}

The location variable defined in CGObject is used as the upper-left corner of the rectangle. The lr variable defined by CGBox is the lower-right corner of the rectangle. CGBox provides two constructors. The first takes arguments for the upper-left and lower-right corners of the rectangle and a character to be used as the drawCharacter. The second constructor provides for a default box drawCharacter and uses a call to the first constructor to accomplish its initialization. The display() method displays a box on an object of class PrintCGrid. It is a good example of object composition because it uses objects of several different classes to accomplish this purpose. It begins by calculating the box's width and height dimensions from the location and lr variables. The location variable is the upper-left corner of the box, and the lr variable is the lower-right corner. It then creates two points, topRow and bottomRow, that will be used to step through the top and bottom rows of the box's display. The first for statement is used to display the box's top and bottom rows. The putCharAt() method of CGrid is used to display the drawCharacter at the locations specified by topRow and bottomRow. The putCharAt() method is inherited by objects of class PrintCGrid. The x-coordinates of the topRow and bottomRow variables are incremented by 1 to step through the rows' display using the add() method of the Point class. The display() method creates the leftCol and rightCol points to be used to step through the display of the box's left and right columns. The second for statement displays these columns in a similar manner to the first for statement. The y-coordinates of the leftCol and rightCol variables are incremented by 1 to step through the columns' display. The describe() method displays a description of a box's parameters to the console window. This description identifies the drawCharacter and upper-left and lower-right corners of the box.

The CGText Class

The CGText class is the third and final subclass of CGObject that is declared in this chapter. (See Listing 5.8.) The CGObject class hierarchy is extended further in Chapter 6.
Listing 5.8. The CGText class source code. package jdg.ch05;

// CGText.java

public class CGText extends CGObject {
 // Variable declarations
 String text;

 // Method declarations
 public CGText(Point p,String s) {
  location = p;
  drawCharacter = ' ';
  text = s;
 }
 public void display(PrintCGrid grid) {
  Point p = new Point(location);
  for(int i=0;i<text.length();++i){
   grid.putCharAt(text.charAt(i),p);
   p = p.add(1,0);
  }
 }
 public void describe() {
  System.out.println("CGText "+location.toString()+" "+text);
 }
}

CGText declares one variable, text, that is used to store a string of text to be displayed on the grid. It has a single constructor that takes two arguments: a Point value that identifies the point on the grid where the text is to be displayed and a String value that specifies this text. Because drawCharacter is not displayed, it is initialized to a space. CGText implements the two abstract methods required of it. The display() method displays the text variable at the location specified by the location variable. The describe() method displays the location of the point and its text to the console window.

The KeyboardInput Class

The KeyboardInput class extends the DataInputStream class of the Java API to provide a set of common simple methods for getting keyboard input from the user. (See Listing 5.9.)
Listing 5.9. The KeyboardInput class source code. package jdg.ch05;

import java.lang.System;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.IOException;

public class KeyboardInput extends DataInputStream {
 public KeyboardInput(InputStream inStream) {
  super(inStream);
 }
 public char getChar() throws IOException {
  String line = readLine();
  if(line.length()==0) return ' ';
  return line.charAt(0);
 }
 public String getText() throws IOException {
  String line = readLine();
  return line;
 }
 public int getInt() throws IOException {
  String line = readLine();
  Integer i = new Integer(line);
  return i.intValue();
 }
 public Point getPoint() throws IOException {
  System.out.print(" x-coordinate: ");
  System.out.flush();
  int x = getInt();
  System.out.print(" y-coordinate: ");
  System.out.flush();
  int y = getInt();
  return new Point(x,y);
 }
}

KeyboardInput has a single constructor that takes an object of class InputStream as a parameter. This object should be java.lang.System.in, but may be mapped to other input streams if necessary. The KeyboardInput constructor passes the InputStream object to DataInputStream's constructor using the super() constructor call. KeyboardInput defines four access methods that get objects of type char, String, int, and Point from the user. The getChar() method uses the readLine() method of DataInputStream to read a line of data entered by the user. If the line is blank, it returns the space character; otherwise it returns the first character in the input line. The getText() method simply returns the entire line entered by the user, whether it is blank or not. The getInt() method works the same way as getChar() except that it uses a constructor of the Integer class to construct an object of class Integer directly from the input line. It then converts the Integer object to an object of type int before it returns it using the return statement. The Integer class is an example of a class that wraps the primitive type int. Class wrappers are covered in Chapter 12, "Portable Software and the java.lang Package." The getPoint() method interacts with the user to get the x- and y-coordinates of a point. It then constructs an object of class Point that it uses as its return value. The getPoint() method uses the getInt() method to get the values of the x- and y-coordinates.

The CDrawApp Program

You're finally ready to use all the classes that you've developed in this chapter to build the CDrawApp program. Make sure that you've compiled all the classes that have been introduced. Your c:\java\jdg\ch05 directory should have compiled classes for Point.java, CGrid.java, CGObject.java, PrintCGrid.java, BorderedPrintCGrid.java, CGPoint.java, CGBox.java, CGText.java, and KeyboardInput.java. The CDrawApp.java file is shown in Listing 5.10.
Listing 5.10. The CDrawApp and CDraw classes. package jdg.ch05;

import java.lang.System;
import java.io.DataInputStream;
import java.io.IOException;

class CDrawApp {
 public static void main(String args[]) throws IOException {
  CDraw program = new CDraw();
  program.run();
 }
}

class CDraw {
 // Variable declarations
 static KeyboardInput kbd = new KeyboardInput(System.in);
 BorderedPrintCGrid grid;

 // Method declarations
 CDraw() {
  grid = new BorderedPrintCGrid();
 }
 void run() throws IOException {
  boolean finished = false;
  do {
   char command = getCommand();
   switch(command){
    case 'P':
     addPoint();
     System.out.println();
     break;
    case 'B':
     addBox();
     System.out.println();
     break;
    case 'T':
     addText();
     System.out.println();
     break;
    case 'U':
     grid.deleteLastObject();
     System.out.println();
     break;
    case 'C':
     grid.clearGrid();
     System.out.println();
     break;
    case 'S':
     grid.show();
     break;
    case 'X':
     finished = true;
    default:
     System.out.println();
   }
  } while (!finished);
 }
 char getCommand() throws IOException {
  System.out.println("CDrawApp P - Add a Point U - Undo Last Add");
  System.out.print("Main Menu B - Add a Box C - Clear Grid");
  System.out.println(" X - Exit CDrawApp");
  System.out.print(" T - Add Text S - Show Grid");
  System.out.print(" Enter command: ");
  System.out.flush();
  return Character.toUpperCase(kbd.getChar());
 }
 void addPoint() throws IOException {
  System.out.println("Add Point Menu");
  System.out.println(" Location:");
  Point p = kbd.getPoint();
  System.out.print(" Character: ");
  System.out.flush();
  char ch = kbd.getChar();
  if(ch==' ') ch = '+';
  CGPoint cgp = new CGPoint(p,ch);
  cgp.addToGrid(grid);
 }
 void addBox() throws IOException {
  System.out.println("Add Box Menu");
  System.out.println(" Upper Left Corner:");
  Point ul = kbd.getPoint();
  System.out.println(" Lower Right Corner:");
  Point lr = kbd.getPoint();
  System.out.print(" Character: ");
  System.out.flush();
  char ch = kbd.getChar();
  if(ch==' ') ch = '#';
  CGBox box = new CGBox(ul,lr,ch);
  box.addToGrid(grid);
 }
 void addText() throws IOException {
  System.out.println("Add Text Menu");
  System.out.println(" Location:");
  Point p = kbd.getPoint();
  System.out.print(" Text: ");
  System.out.flush();
  String text = kbd.getText();
  CGText cgt = new CGText(p,text);
  cgt.addToGrid(grid);
 }
}

The declaration of the CDrawApp class is very small. It consists of a main() method that creates an object of class CDraw and then invokes the run() method for that object. A separate CDraw object is created because the main() method is static. static methods are like static variables. They apply to the class as a whole and not to objects that are individual instances of a class. static methods can only access variables that they create or that are static variables of the class. By creating the CDraw class, you are able to avoid any limitations posed by static's main() method. The CDraw class declares two variables: kbd and grid. The kbd variable is used to get input from the user. The grid variable is used to display objects such as points, boxes, and text. The kbd variable is created as a static variable of class KeyboardInput. It is initialized using the KeyboardInput() constructor and the predefined java.lang.System.in input stream. A variable initializer is used to create and initialize the value of kbd. Other initializers, called static initializers, are also supported by Java. Static initializers allow blocks of statements to be executed during class initialization. They are covered in Chapter 11, "Language Summary." CDraw has a single constructor that creates an object that is a new instance of BorderedPrintCGrid and assigns it to the grid variable. The BorderedPrintCGrid() constructor creates a default grid 75 characters wide and 20 characters high. The default border character is an asterisk (*). CDraw has five access methods. The run() method implements the core processing of the CDrawApp program. It uses a do statement that repeatedly processes user keyboard commands. It invokes the getCommand() method to display a menu to the user and retrieve the user's command selection. It then uses a switch statement to process the command. It invokes the addPoint(), addBox(), and addText() methods to add points, boxes, and text to the grid. It invokes the deleteLastObject() and clearGrid() methods of the PrintCGrid class to remove the last object added to the grid or to completely clear the grid of all objects. The show() method of PrintCGrid is used to draw and display the grid. If the user enters a command line beginning with X or x, the boolean variable finished is set to true, the do statement finishes, and the CDrawApp program terminates. The getCommand() method displays a menu to the user and uses the getChar() method of the KeyboardInput class to get a character command from the user. The static toUpperCase() method of the Character class is used to convert the character returned by getChar() to uppercase. The addPoint() method queries the user to enter the location of a point and the character to be used to displayed at that location. It uses the getPoint() and getChar() methods of the KeyboardInput class. If a user enters a space for the display character, addPoint() uses the plus (+) character as a default. It uses the data obtained from the user to construct an object of class CGPoint and adds the object to grid using the addToGrid() method of class CGObject that is inherited by class CGPoint. The addBox() method is similar to the addPoint() method except that it must obtain two points from the user-the upper-left and lower-right corners of a rectangle. It also obtains a box display character from the user, the default value of which is the pound (#) sign. An object of class CGBox is constructed from the user-supplied information and added to the grid using the CGObject addToGrid() method. The final method of CDraw, addText(), retrieves a location and a text string from the user and uses this information to create an object of class CGText. The new object is then added to the grid in the same manner as the CGPoint and CGBox objects.

Running CDrawApp

Assuming that you have compiled all the classes introduced in this chapter, go ahead and compile CDrawApp. You can then run CDrawApp using the following command line:
C:\java\jdg\ch05>java jdg.ch05.CDrawApp
CDrawApp begins by displaying the following menu:
CDrawApp    P - Add a Point   U - Undo Last Add
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command:
This menu provides you with seven command options: P, B, T, U, C, S, and X. Entering X will cause CDrawApp to terminate. You don't want to do this yet. Entering S will cause CDrawApp to display the character grid. Go ahead and enter S. Your screen should look like this:
***************************************************************************
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
***************************************************************************
CDrawApp    P - Add a Point   U - Undo Last Add
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command:
CDrawApp displays a blank bordered grid using the show() method of the PrintCGrid class. You should be able to trace program execution up to this point by examining the source code files of the classes used in this chapter. The CDraw run() and getCommand() methods perform most of the user interface processing. You can add a point to the grid by entering a P command. You will get the following display:
Add Point Menu
 Location:
  x-coordinate:
The Add Point Menu prompt is displayed by the addPoint() method of the CDraw class. It prompts you to enter the x-coordinate of a grid point. The upper-left corner of the grid is 0,0 and the lower-right corner of the grid is 74,19, where 74 is the maximum x-coordinate and 19 is the maximum y-coordinate. Enter 35 for the x-coordinate. The Add Point Menu then prompts you to enter the y-coordinate:
Add Point Menu
 Location:
  x-coordinate: 35
  y-coordinate:
Enter 10 for the y-coordinate. You are prompted to enter the character to be displayed at location 35,10. Enter x to finish adding a point:
Add Point Menu
 Location:
  x-coordinate: 35
  y-coordinate: 10
 Character: x
CDrawApp    P - Add a Point   U - Undo Last Add
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command:
The CDrawApp main menu is displayed again. To verify that the point you just entered was, in fact, added to the grid, redisplay the grid by entering S. You will see the x in the middle of the grid:
***************************************************************************
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                   x       &n bsp;                               *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
*                                          &nb sp;                                *
***************************************************************************
CDrawApp    P - Add a Point   U - Undo Last Add
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command:
Now use the B command to enter a box:
CDrawApp    P - Add a Point   U - Undo Last Add
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command: b
Add Box Menu
 Upper Left Corner:
  x-coordinate:
You will have to enter two points and a character to specify a box. Enter 5 for the x-coordinate of the upper-left corner and 1 for its y-coordinate:
Add Box Menu
 Upper Left Corner:
  x-coordinate: 5
  y-coordinate: 1
 Lower Right Corner:
  x-coordinate:
Enter 70 for the x-coordinate of the lower-right corner and 18 for its y-coordinate:
Add Box Menu
 Upper Left Corner:
  x-coordinate: 5
  y-coordinate: 1
 Lower Right Corner:
  x-coordinate: 70
  y-coordinate: 18
 Character:
Finally, set the box character to the equals sign (=):
Add Box Menu
 Upper Left Corner:
  x-coordinate: 5
  y-coordinate: 1
 Lower Right Corner:
  x-coordinate: 70
  y-coordinate: 18
 Character: =
CDrawApp    P - Add a Point   U - Undo Last Add
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command:
Go ahead and redisplay the grid using the ShowGrid command:
***************************************************************************
*                                          &nb sp;                                *
*     ==================================================================    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                             x        & nbsp;                         =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     ==================================================================    *
*                                          &nb sp;                                *
***************************************************************************
CDrawApp    P - Add a Point   U - Undo Last Add
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command:
Notice how the box was added to the grid. Now, let's add text to the grid. Enter T to bring up the Add Text Menu prompt:
Add Text Menu
 Location:
  x-coordinate:
Set the x-coordinate to 36 and the y-coordinate to 11:
Add Text Menu
 Location:
  x-coordinate: 36
  y-coordinate: 11
 Text:
Enter I love Java. at the Text: prompt:
Add Text Menu
 Location:
  x-coordinate: 36
  y-coordinate: 11
 Text: I love Java.
The CDrawApp main menu is displayed. Use the Show Grid command to redisplay the grid:
***************************************************************************
*                                          &nb sp;                                *
*     ==================================================================    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                             x                                  =    *
*     =                             I love Java.                       =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     =                                     &n bsp;                          =    *
*     ==================================================================    *
*                                          &nb sp;                                *
***************************************************************************
CDrawApp    P - Add a Point   U - Undo Last Add
Main Menu   B - Add a Box     C - Clear Grid      X - Exit CDrawApp
            T - Add Text      S - Show Grid       Enter command:
Enter U to invoke the Undo Last Add command. This results in the text being deleted from the display. Verify this by redisplaying the grid. Then clear the grid by entering C. Once again, use the Show Grid command to verify that the command worked correctly. You have now covered all the commands of CDrawApp. Enter X to exit the program.

CDrawApp's Implementation of Object-Oriented Concepts

The purpose of CDrawApp isn't to bolster your graphics production capabilities. It is used as a comprehensive example of how Java classes, objects, and methods can be used to implement the object-oriented programming concepts studied earlier in the chapter. In building CDrawApp, you created 11 classes, 6 of which extended classes other than Object. The class hierarchy for the CDrawApp program is shown in Figure 5.4. Figure 5.4 : The class hierarchy. The development of the CGrid, PrintCGrid, and BorderedPrintCGrid classes shows how subclasses extend the data and methods inherited from their parents to successively add more features to their branch of the class hierarchy. CGrid provides the basic data and methods to implement character grid objects. PrintCGrid adds the capability to add and remove objects from the grid, and to display these objects on the console window. BorderedPrintCGrid uses the methods provided by CGrid and PrintCGrid to develop additional capabilities for displaying a bordered grid. The CGObject, CGPoint, CGBox, and CGText classes are examples of how abstract classes are used to specify the behavior of their subclasses. CGObject provides the addToGrid() method, which is inherited by all of its subclasses. It defines the display() and describe() methods as abstract, requiring all subclasses to implement these methods in a manner that is applicable to the subclass. CGPoint, CGBox, and CGText define specific types of graphical objects that fit within the framework established by CGObject. The drawGrid() method of PrintCGrid utilizes the CGObject class as an abstraction for dealing with objects in the displayList[]. The dynamic binding and polymorphic capabilities of the Java compiler and runtime system enable drawGrid() to interact with objects of subclasses of CGObject without specific knowledge of their class type. The grid object of class BorderedPrintCGrid that is used in the CDraw class provides an example of advanced polymorphic behavior. When grid's show() method is invoked, the show() method of the PrintCGrid class is used. The show() method invokes the PrintCGrid drawGrid() method to cause each object in the displayList[] to display itself on the grid. It then invokes the displayGrid() method to display the grid. BorderedPrintCGrid overrides the displayGrid() method of PrintCGrid. Which displayGrid() method does show() invoke-that of PrintCGrid or BorderedPrintCGrid? The show() method is able to discern from runtime information that it is being invoked for an object of class BorderedPrintCGrid, so it uses the BorderedPrintCGrid displayGrid() method. This method checks to see if the useBorder variable is true, and if so, displays a grid with a border. However, if useBorder is false, it invokes the displayGrid() method of PrintCGrid. The Point class is an example of encapsulation. It has a modular, well-defined interface and hides the details of its implementation from other classes. The x- and y-coordinates of a point are inaccessible to methods outside of the Point class. Even the methods within Point use the x() and y() methods to access these values. The Point class, because of this encapsulation, can be reused in many other applications that use two-dimensional grids. The KeyboardInput class shows how classes from the Java API may be extended by user-defined subclasses. It uses the readLine() method from DataInputStream, the charAt() method from the String class, and the intValue() method of the Integer class to provide convenient methods for accessing user input lines. The CDraw and CDrawApp classes illustrate the principle of object composition. They assemble the other classes defined in this chapter into a short, two-page program that uses all of the features provided by these classes.

Summary

In this chapter you have learned some general object-oriented programming concepts and how these concepts apply to Java programs. You have learned how to use classes to develop a sample Java program that illustrates the fundamental elements of Java object-oriented programming. The classes you developed in this chapter will be reused in Chapters 6 and 7. In Chapter 6 you'll learn how to use interfaces to support features of multiple inheritance. In Chapter 7 you'll learn how to use exceptions to respond to errors and other anomalies