Laboratory Exercises For Computer Science 153

Generalization, Polymorphism, and Exceptions

Generalization, Polymorphism, and Exceptions

Consider the problem of maintaining a directory of students, staff, and faculty. For the purposes of this lab, suppose an entry in the directory has the following fields:

The directory should contain these methods:

Some Observations

This problem illustrates some common characteristics of a variety of applications. At one level, we want to consider all people in the directory as being similar -- the directory should be a collection of entries. At another level, each type of person (e.g., students, staff, faculty) has special characteristics.

One way to accommodate such multiple levels is develop an appropriate class hierarchy. In this case, we might begin with an Entry class and then define three subclasses, Student, Staff and Faculty. Schematically, this is shown in the following diagram:

Noting that both Staff and Faculty have offices and office extensions, this hierarchy might be refined further, as follows:

For an extended example, this alternative hierarchy might have several advantages as more common characteristics of staff and faculty were identified. For simplicity in what follows, however, we adopt the first of these class hierarchies, focusing on classes Entry, Student, and Faculty for individuals and class SchoolDirectory for the overall structure. The development of the Staff class is left as an exercise.

The Entry Class

The Entry class should contain the data and capabilities that we might want for the records of all types of people. In reviewing the problem, some obvious methods involve the creation of an entry, printing a record, and checking if an entry's name matches a given name. In addition, if names are to be stored alphabetically, it will be convenient to be able to compare if one entry equals another or if one entry comes before another alphabetically. Program Entry.java contains a simple version of such an Entry class.

Design Principle: An object or a class is a self-contained entity, and it should include those data elements and operations that naturally support that entity. For the Entry class, we identify both data and operations which seem closely related to each other and to our image of what directory entries might contain.

As you review this Entry class, note that it contains its own testing code in its main method. When this code is run by itself, this main method will run to provide approprate testing. When Entry is imported and another class is run, then that class's main will provide the basis for program execution.

  1. Copy Entry.java to your account, compile and run it, and check that you understand how it works.

The Student Subclass

With Entry defined, we can define the Student class by extending Entry and adding only the few new required fields. The resulting code is Student.java

For the most part, the code in Student.java is straightforward. However, in two places, we want to utilize existing code in super class Entry as a first step in later processing. Specifically, in initializing a Student, we first call upon Entry's constructor to initial the inherited fields, firstName, lastName, and eAddress. Such a reference to a method in a super class uses the keyword super. Similarly, a reference to the print method in the super class Entry is given by super.print.

  1. Copy Student.java to your account. Again, compile and run the program, and check that you understand how it works.

The Faculty Subclass

Program Faculty.java provides an implementation of the Faculty class, analgous to Student.
  1. Copy Faculty.java to your account, run it, and review how it works.

  2. In Faculty.java, the current print method relies on Entry's print method to begin the process. Suppose, however, that we want to change the format of printing, so a faculty member's department follows on the line immediately after the person's name rather than later in the listing. Modify print in Faculty.java to make this change. DO NOT CHANGE Entry in any way!

The School Directory Class

Before writing the SchoolDirectory class, we must decide how to store entries. As suggested earlier, one approach is to keep the entries, arranged alphabetically by name, in an array -- but what size should the array be? One approach is to create an array of some size maxSize and maintain a separate variable currentSize. We can keep adding entries to the array until currentSize equals maxSize. At this stage, we could generate a larger array (perhaps twice the size), copy the old array to the new, larger one, and then make the insertion. Such an approach does not place an arbitrary limit on the directory size, but also does not waste space excessively.

With this approach to array expansion, we can insert new elements in name order, following the insertion process from an insertion sort. Printing can follow a linear scan of the array, and data retrieval can follow from a binary search. For data retrieval, however, we still must decide what the search should return. A common approach would yield the entry in question, so the program could do subsequent processing if desired. All of this work follows algorithms and approachs seen previously in the course, and we can present a first version of program SchoolDirectory.java.

Inheritance, is-a Relationships, and Polymorphism

While the coding within SchoolDirectory.java follows directly, the resulting program illustrates several important points:

  1. Add an update capability to this application, as follows:

    1. Add an update method to class Entry and each of its subclasses to provide a framework to change data within an entry (except the name). update should use no parameters. Rather, the method should prompt the user at the keyboard for new values of all fields within the Entry, except for the person's name which should remain unchaned.

    2. Add an update method to class SchoolDirectory. This update method should use two String parameters, for the person's first and last name. update then should use the lookup to locate the correct Entry within the directory, and the Entry's update method to effect the change. If the specified name is not in the directory, a warning message should be printed.

  2. Add a remove method to class SchoolDirectory to delete a person with a given first and last name from the directory.

  3. Extra Credit: Write a Staff class which extends Entry, according to the field specification given at the start of this lab.

Exceptions and Exception Handling

While class SchoolDirectory worked reasonably well, the lookup method contains some awkwardness. In particular, the method is supposed to return a specified Entry. If the person is found, the method works as desired. The difficulty arises if the person is not present.

In SchoolDirectory.java, lookup resorts to returning a null value if no such person is found. While this approach satisfies the specification of the method by yielding a special value, this approach can cause additional effort in an application, as shown in the corresponding main method. Specifically, lookup always returns something, and that object must be tested each time to determine if the returned value is null.

A cleaner approach is to break the normal flow of processing, using an exception. In general, an exception is any event that may require a change in the normal flow of processing. In Java, a program "throws" an exception when the unusual event is detected. Statements in the block calling the method then "catch" the exception, allowing appropriate action to be taken. To illustrate this approach for the SchoolDirectory example, program SchoolDirectoryAlt.java takes advantage of a NoSuchElementException, already built into Java in the java.util class.

In this revised program, the return null statement at the end of the lookup method is replaced by


  // if person not found, generate an exception
  throw new NoSuchElementException();

With this addition, normal processing is interrupted if the desired name is not found. Throwing the exception allows the end of the main method to be much cleaner. The main structure for this code is


try {
.
.
.
dir.lookup("- - - -", "- - - -");
.
.
.
} catch 
    (NoSuchElementException e) {
        out.println("Directory Entry not found:  ");
        out.println("   Exception caught:  " + e);
}

In this try ... catch block, the try keyword indicates that processing should shift to the catch section if any exceptions are encountered in the block. The block itself then can be considerably simpler.

More generally, a program can contain several try ... catch blocks (even one for each lookup), and exceptions can be handled differently in each one if desired.

  1. Modify the update and remove methods in SchoolDirectory to respond appropriately if lookup throws a NoSuchElementException exception. Note that your changes should effect the methods (and testing) in SchoolDirectory only. No changes should be made in Entry or any of its subclasses.

Work To Turn In:


This document is available on the World Wide Web as

http://www.walker.cs.grinnell.edu/courses/153.sp00/lab-generalization.html

created May 2, 2000 by Henry M. Walker
last revised May 3, 2000
Henry Walker (walker@cs.grinnell.edu)