Methods

A method specifies a sequence of actions to be performed by a processor. A method declaration consists of a heading and a body. The heading specifies the name, return type, and parameters of the method. The body is composed of statements that describe the actions to be performed when the method is executed.

Method heading

A method header specifies the type of value returned by the method (if the method returns a value), the name of the method, and a list of method parameters. The parameter list is a possibly empty sequence of automatic variable declarations, separated by commas and enclosed in parentheses. Examples are:

    int firstInstance (Object obj, List list)
    int length ()

Both these headings specify that the type of the value returned by the method is int. The first has two parameters, an Object and a List. The second has no parameters.

If the method does not return a value, it is specified as void. For instance:

    void setLength (int length)

The signature of a method consists of the method name and the number and types of the parameters. The signatures of the methods given above are

    firstInstance(Object, List)
    length()
    setLength(int)

A class may not declare two methods with the same signature.

A method header may also contain a throws clause following the parameter list. This consists of the key word throws followed by a comma separated list of exception class names:

    public void appendToFile (File f, char c)
        throws IOException, FileNotFoundException

If it is possible for a checked exception to be thrown by the method, and the method does not catch this exception, a throws clause naming the exception must be included in the heading. (A checked exception is an instance of the class Exception that is not also a RuntimeException.) For a detailed explanation, see the section on checked and unchecked exceptions or section 18.2 of Niño-Hosch.

Method body

A method body is a block: that is, a possibly empty sequence of statements and local variable declarations enclosed in braces. The heading and body make up a method declaration. For instance:

    int firstInstance (Object obj, List list) {
        int index;
        index = 0;
        while (index < list.size() && !obj.equals(list.get(index)))
            index = index+1;
        if (index == list.size())
            return -1;
        else
            return index;
    }

Method invocation

A method invocation consists of the method name followed by an argument list. The method name can be a simple or qualified name. A simple name invokes a method defined in the same class as the invocation. A qualified name identifies an object or class that defines the method.

The argument list is a possibly empty sequence of expressions, separated by commas and enclosed in parenthesis.

Consider the following examples.

If a method returns a value, an invocation of the method is an expression:

    int size = length();

A method invocation followed by a semicolon is a statement:

    target.setLength(max-min+1);

An invocation of a method specified as void can only by used as a statement.

When a method is invoked, the arguments are evaluated and assigned to the parameters. Then the body of the method is executed.

Argument-parameter binding

The arguments in the invocation must match the parameters in the method definition in type and number. For example, an invocation of the method setLength specified above must have a single integer argument. An invocation of firstInstance must have two arguments: the first, a reference to an Object (or subclass of Object), and the second a reference to a List (or subclass of List).

Arguments are associated with parameters by a mechanism sometimes referred to as "call by value." When a method is called, a variable is created for each parameter. The arguments are evaluated, and the argument values are assigned to the parameter variables. Thus the parameters are initialized with the values of the arguments.

Consider the following example, which unsuccessfully attempts to interchange the values of two integer variables.

    void swap (int x, int y) {
        int temp;
        temp = x;
        x = y;
        y = temp;
    }

Suppose that a and b are int variables, containing the values 3 and 4 respectively,

and that the method swap is invoked with a and b as arguments:

    swap(a,b);

Invoking the method causes new variables x and y to be created: x is initialized to 3 (the value of a), and y is initialized to 4 (the value of b).

Executing the method interchanges the values of the variables x and y, but does not effect the variables a and b. (Hmmm. Can you write a method that will interchange the values of two variables?)

Note that the names of the variables don't matter. If the parameters were named a and b, they would still be different variables from those used as arguments.

If the parameter is a reference type, then the argument-parameter binding behaves in a manner similar to that sometimes described as "call by reference." Consider the class Point, with instance variables x and y:

    public class Point {
        public int x;
        public int y;
    }

The following method requires a reference-to-Point value as argument:

    void reflect (Point p) {
        int temp;
        temp = p.x;
        p.x = p.y;
        p.y = temp;
    }

If center is a reference-to-Point variable, then when reflect is invoked with the call

    reflect(center);

the parameter p will reference the same object as the variable center:

Automatic variables: lifetime

A local variable is a variable declared inside a method. A local variable's declaration consists of a type and identifier followed by a semicolon. For instance,

    int count;
    List schedule;
    Observer watcher;
    double[] vector;

A declaration can contain a variable initializer, which is an assignment operator followed by an expression:

    int count = 0;
    List schedule = null;
    int size = max-1;
    Student valedictorian = school.getRanked(0);

(For details on array initializers, see the section on arrays.)

Local variables and parameters are called automatic variables. They are (effectively) created when the method containing their declaration is invoked, and are de-allocated when the method completes execution. Thus their lifetime is the duration of the method execution.

Since a method can be invoked a second time before it has completed execution, there may be several "instances" of a variable in existence simultaneously. Consider the following recursive method:

    int factorial (int n) {
        int temp;
        if (n == 0)
            return 1;
        else {
            temp = factorial(n-1);
            return temp*n;
        }
    }

If the method is invoked with an argument of 3, automatic variables named n and temp are allocated, with n initialized to 3. Before the method completes, it invokes itself with an argument of 2. Thus another pair of variables named n and temp are allocated, with n initialized to 2. Expression evaluation uses the most recently allocated existing instance of a variable. For details of recursive methods, see chapter 17 of Niño-Hosch.

Automatic variables: scope

The scoping rules of a language associate applied occurrences of an identifier with defining occurrences. A defining occurrence is the introduction of an identifier in a declaration to name the entity being defined. For instance, in the method declaration

    void reflect (Point p) {
        int temp;
        temp = p.x;
        p.x = p.y;
        p.y = temp;
    }

the first occurrences of the identifiers reflect, p and temp are defining occurrences. They are introduced to name a method, parameter, and local variable respectively. All other identifier occurrences in the method are applied occurrences: they are used to reference the named entity.

The scope of a declaration is the section of program text in which applied occurrences of an identifier refer to the identifier introduced by the declaration. The scope of a local variable declaration is from the declaration to the end of the compound statement (or method body) containing the declaration. A parameter is treated like a local variable defined just inside the method body. Thus the scope of parameter definition is the method body.

The scoping rules tell us that the variable temp referred to in the first assignment statement, is the int variable declared in the previous statement. As the scope of the declaration is from the declaration to the end of the method body. It would not be correct to write the declaration after the assignment:

    void reflect (Point p) {
        temp = p.x;
        int temp;
        ...

Now the occurrence of temp in the assignment statement is outside of the scope of the declaration.

Also notice that

    int maxIndex = 3;
    int size = maxIndex+1;

is correct, while the following is not

    int size = maxIndex+1;
    int maxIndex = 3;

Though a method can contain several distinct declarations of variables with the same name, their scopes cannot overlap. For example, the following method contains legal declarations of two variables named sum:

    double halve (int x) {
        double result;
        if (x > 1) {
            int sum = x;
            result = sum/2;
            // scope of int sum ends here!
        } else {
            // declaration of another variable named sum is ok.
            double sum = x;
            result = sum/2;
        }
        return result;
    }

while this one does not:

    double halve (int x) {
        double result;
        double sum = x;
        if (x > 1) {
            // still in the scope of double sum!
            // declaration of another variable named sum not legal.
            int sum = x;
            result = sum/2;
        } else {
            result = sum/2;
        }
        return result;
    }

Finally, an identifier prefixed with " something." is never a reference to an automatic variable. For instance, in the following method

    void setSize (int size) {
        this.size = size;
    }

the occurrence of size on the right hand side of the assignment refers to the parameter, while the occurrence of size on the left (this.size) does not.

Local variables: must be initialized before use

The value of a local variable that is declared without an initializer is undefined. It is not legal to try to use the value of an undefined variable. For example, in the following

    int temp;
    temp = temp+1;

the variable temp is undefined when the expression temp+1 is evaluated. The compiler will report this as a syntactic error.

The compiler insists that a variable be defined along every path leading to an expression in which its value is used. For instance, the following is also syntactically incorrect, since the assignment incrementing temp will be executed without initializing temp if x is negative.

    int temp;
    if (x > 0)
        temp = 1;
    temp = temp+1;

The requirement that a local variable be explicitly initialized before use also applies to reference variables. For instance, the following is not legal since obj is used in the expression obj == null without being initialized.

    Object obj;
    if (obj == null)
        ...

Scope modifiers

A method can be specified as private, public, or protected, by preceding the declaration with the appropriate key word:

    public int length () ...
    private void setLength (int size) ...

If no scope modifier is specified, the method is restricted. See the section on scope modifiers of class members or section 14.6 of Niño-Hosch for details.

In an interface, a method specified without a scope modifier is public, not restricted.

Final methods

A method specified as final cannot be overridden or hidden in a subclass. A method is specified as final by prefixing the keyword final:

    final public void dispose () ...

Static methods

A method specified as static is a class method. That is, the method is associated with the class itself rather than with an instance of the class. A static method cannot reference instance variables or non-static methods of the class.

    static public void condition (boolean condition) ...

Abstract methods

A method can be specified as abstract in an abstract class or interface, in which case its body is replaced by a semicolon:

    public abstract void get (int i);

In an interface, the key word abstract need not be written.

Order of modifiers

Scope, final, static, and abstract modifiers can be written in any order. For instance, the specifications

    public abstract int size ();

and

    abstract public int size ();

are equivalent.


Fred Hosch
Last modified: Tue Aug 7 08:22:39 CDT 2001