Contents:


Introduction

This handout describes how to document the specifications of classes and methods. This document focuses on practical issues.

This document uses the Line class as an example. Notice that we do not provide fields or method bodies in our example. This document covers specifying the behavior of classes and methods (what they should do), not their implementation (what they actually do and how they do it). Abstraction Functions and Representation Invariants covers how to document a class's implementation.


/**
 * This class represents the mathematical concept of a line segment.
 *
 * Abstract Invariant:
 *  A line's start-point must be different from its end-point.
 */
public class Line {

  ... // Fields not shown.

 /**
  * @requires p != null && ! p.equals(start-point)
  * @modifies this
  * @effects Sets end-point to p
  */
  public void setEndPoint(Point p) {
    ...
  }

  ...

}

Because the concepts discussed here are interrelated, it is best to start with a short list of definitions of before diving into the details.

Abstract Value
What an instance of a class is suppose to represent. For example, each instance of Line represents a given line.
Abstract State
The information that defines the abstract value. For example, each abstract line has a starting point and an ending point.
Abstract Invariant
A condition that must be true over the abstract state of all instances of a class. For example, Line requires that no line may have the same starting and end point. Abstract invariants are over a class's abstract state.
Method Specifications
Describe a method's behaviors in terms of abstract state. For example, Line's setEndPoint method reassigns the end-point specification field.

The above concepts are included in a class's external specification (in Javadoc). They aid a client with using the class.

The rest of this document is organized as follows. First, it explains how to document what a class abstractly represents using abstract state.Then, it explains how to specify method behavior, in terms of abstract state.

Abstract Values and Abstract State

To a computer, an instance of an object is a series of zeros and ones. To humans, on the other hand, these zeros and ones represent higher-level abstract ideas: integers, strings, lines, and lists, for example. The abstract value of an object is what the object represents to humans on this abstract level.

The abstract state of an object is what information a humans associate with the abstract value of an object. An abstract value is determined by the abstract state. For example, for the String "cat", humans associate the following sequence of letters: 'c', 'a', and 't'. This sequence of letters is the abstract state of the String.

It is important to note that how the String "cat" is represented concretely (within the Java code) does not affect its abstract state. Whether String internally uses an array, a List or some clever compressed numerical encoding for the the characters 'c', 'a', and 't', the abstract state of "cat" is still a just a sequence of those letters.

Mathematical Abstract Values

For some data types (classes), the abstract value space consists of familiar mathematical objects:

If so, you're in luck. You can use conventional notation for explaining the class's abstract values and functions on its abstract value (see later sections).

You aren't obliged to use this syntax. Some of it is more standard than the rest: everybody with some training in mathematics recognizes set comprehension syntax, but sequence concatenation isn't particularly standardized. You may find it clearer to write sequence concatenation as a function like concat(x, y). What really matters is clarity and lack of ambiguity, so if you have any doubt whether your reader will understand you, just define it: "...where concat(x,y) is the concatenation of two sequences x and y."

Method Specifications

Method specifications describe the behavior of a method in terms of its preconditions and postconditions. Note that method specifications may only refer to specification fields, method arguments, and global variables (aka public static fields), never to the concrete fields of the implementation.

Preconditions

Preconditions are properties that must be true when the method is called. It is the responsibility of the caller to guarantee that these properties hold. If the preconditions do not hold, the method is allowed to behave in absolutely any fashion, including crashing the program, continuing with incorrect results, informing the user of the problem, or gracefully recovering from the problem. Callers should always assume that preconditions are not checked by a method. However, it is good practice — though not required — for a method to check its preconditions (if the check can be performed efficiently) and throw an error if they are not satisfied.

Preconditions are indicated by the "requires" clause in a method specification. If a "requires" clause is omitted from the method specification, it is assumed that the method does not have any preconditions.

requires (default: no constraints)
The preconditions that must be met by the method's caller.

Postconditions

Postconditions are properties that a method guarantees will hold when the method exits. However, if the precondition did not hold when the method was called, then all bets are off, and the method may behave in any fashion whatsoever. In particular, if the precondition does not hold upon method entry, then the postcondition need not hold on method exit.

A postcondition can be written as a single complex logical formula, but it is convenient to separate it into logically distinct parts. CSE 331 uses "return", "effects", "throws", and "modifies". (In the descriptions below, "default" indicates what is assumed if that clause is omitted from the method specification.)

return (default: no constraint on what is returned)
The value returned by the method, if any.
throws (default: none, which means that no exceptions are ever thrown)
The exceptions that may be raised, and under which conditions.
modifies (default: nothing, which means that there are no side effects)
Variables whose value may be modified by the procedure. They are not guaranteed to be modified, unless otherwise indicated by he effects clause. If object x has specification fields f, g, and h, then "modifies x" means that any combination of x.f, x.g, and x.h might be modified. "modifies x.g, x.h" would be more restrictive. Often, programmers are more interested in quantities that are not listed in the modifies clause, since those are guaranteed not to be changed by the method.
effects (default: true, which means "can have any effect" on the references listed under modifies)
The side effects of the method, such as changes to the state of the current object, to parameters, or to objects held in global variables. If a specification field is listed in the modifies clause but not in the effects clause, then it may take on any value allowed by the abstract invariants of this class of objects. The difference between the modifies and effects causes is that modifies lists everything that may change and effects indicates what changes occur.

Subclasses and overridden methods

A subclass often has a different (stronger) specification than its superclass, and often has a larger abstract state than its superclass. When the specification and abstract state are identical to those of the parent (for instance, an implementation of an abstract class), then there is no need to repeat them in the subclass. However, it is helpful to include a brief note indicating that the superclass documentation should be used instead. That note helps readers to distinguish whether the specification is the same, or the author simply didn't document the class.

When the specifications differ, then you have two options. The first option is to repeat, in the subclass, the full superclass documentation. The advantage is that everything is in one place, which may improve understanding. The second option is to augment the existing specification -- for example, to add a few new specification fields and constraints on them. Whichever you do, make sure that you clearly indicate your approach, and that you are consistent throughout the codebase.

Similar rules hold for a method that overrides another method. It's acceptable to leave the Javadoc blank if the specification is identical. (The generated HTML will use the overridden method's Javadoc documentation, but a normal Java comment is a good hint to someone who is reading the source code.) Otherwise, it is usually better to give the complete specification. If you merely augment the overridden method's specification, be sure to refer to it in the documentation.


Back to the CSE 331 home page.