next up previous
Next: 1.4.4 Inheritance and the Up: 1.4.3 Defining Functional Methods Previous: 1.4.3 Defining Functional Methods

1.4.3.1 An Extended Example

We illustrate this process by defining a method

String findOffice(String name)
on DeptDirectory that finds the office for the person identified by name.

First, we must insert the following member somewhere in the class DeptDirectory

abstract String findOffice(String name);
The abstract modifier in the definition indicates that the method must be implemented in each concrete class extending DeptDirectory.

The ordering of members typically does not affect program behavior. Nevertheless, it is good programming practice to list class members in a consistent order. We recommend placing dynamic members before static members. Within each category, we recommend the following order: fields, constructors, methods. According to this convention, the abstract method findOffice should be the last member in the DeptDirectory class.

Second, we must provide a concrete definition of the findOffice method in each concrete subclass of DeptDirectory, namely Empty and Cons. Note that this method definition format guarantees that a program includes code specifically to process each data variant. Moreover, in any variant containing a field f of parent type, the method typically invokes itself recursively on f. This approach to defining methods is the direct analog of natural recursion template for defining functions in Scheme. It is so common and so important that object-oriented programming methodologies have labeled it as a separate pattern, called the interpreter pattern, enriching the composite pattern.

Let us return to defining the findOffice method. By definition, there is no Entry in an Emtpy directory matching the name passed as an argument to findOffice. Hence, findOffice must return a value signalling failure. In Java, the most convenient choice for such a value is null, the reference to no object. In Java, all object values are actually references, so the same object can simultaneously appear as the value of many different variables. Scheme follows exactly the same convention regarding structures. Java also provides the special value null, which is the reference to no object. Java null should only be used to represent a special failure value. It should never be used to represent one of the alternatives in a data definition, e.g., the empty DeptDirectory. The reason for this prohibition is simple: null is not an object. Any attempt to invoke a method on null will generate a run-time error aborting program execution.

The following code is an appropriate definition of the findOffice method in the Emtpy class.

String findOffice(String name) {
  return null;
}

The definition of findOffice for Cons objects is the only interesting chunk of code in this entire example. Since a Cons object always contains a first Entry and a rest DeptDirectory, the findOffice method must check to see if the passed name matches the name field of first and, depending on the outcome, either return the value of the office or recur on rest.

The object-oriented method has exactly the same recursive structure as the corresponding function definition.

The method can simply be coded as follows:

String findOffice(String name) {
  if (name.equals(first.name)) return first.office;
  else return rest.findOffice(name);
}

Every object contains the method equals which takes a single argument of type Object. For a String object name, the equals method return true if and only if the argument object contains exactly the same sequence of characters as name.

In the code above, the expression

name.equals(first.name)
invokes the equals method of object name on the argument first.name. The expression first.name refers the name field of the object first. Similarly, the expression first.office refers to the office field of the object first and the expression rest.findOffice(name) invokes the findOffice method of object rest on the object name.

Notice that a Java method with n arguments corresponds to a Scheme function of n+1 because the object containing the method is an implicit argument! The implicit argument even has an official name; it is called this. The name this does not appear explcitly in the definition of the method findOffice because the code only refers to specific fields of this rather than the whole object. The definition of findOffice could have been written

String findOffice(String name) {
  if (name.equals(this.first.name)) return this.first.office;
  else return this.rest.findOffice(name);
}
but the explicit references to this make the code harder to read.

Java permits the same name to be used in several different ways at a given point in the program. For example, in the definition of the findOffice method above, the name name refers both to the method argument and a field within Entry objects. Java relies on program context to determine which meaning to use. When context alone is insufficient to determine which meaning is intended, Java follows a fairly simple set of conventions to select a meaning. We will explain these conventions later in this monograph.


next up previous
Next: 1.4.4 Inheritance and the Up: 1.4.3 Defining Functional Methods Previous: 1.4.3 Defining Functional Methods
Robert Cartwright, Spring 1999