4.3 Defining Methods

The general syntax of a method declaration is


<method modifiers> <return type> <method name> (<formal parameter list>)
         <throws clause> // Method prototype
{ // Method body
    <local variable declarations>
    <nested local class declarations>
    <statements>
}

In addition to the name of the method, the method prototype can specify the following information:

  • scope or accessibility modifier (see Section 4.5, p. 121)

  • additional method modifiers (see Section 4.10, p. 144)

  • the type of the return value, or void if the method does not return any value (see Section 5.4, p. 176)

  • a formal parameter list

  • checked exceptions thrown by the method in a throws clause (see Section 5.9, p. 201)

The formal parameter list is a comma-separated list of parameters for passing information to the method when the method is invoked by a method call (see Section 3.17, p. 86). An empty parameter list must be specified by ( ). Each parameter is a simple variable declaration consisting of its type and name:


<parameter modifier> <type> <parameter name>

The parameter names are local to the method (see Section 4.5, p. 123). The parameter modifier final is discussed in Section 3.22 on page 94.

The signature of a method comprises the method name and the formal parameter list only.

The method body is a block containing the local declarations and the statements of the method. Local variable declarations are discussed in Section 2.3 on page 31, and nested local class declarations in Section 7.4 on page 302.

Like member variables, member methods can be characterized as:

  • instance methods, which are discussed later in this chapter

  • static methods, (see Section 4.10, p. 144) which are discussed later in this chapter

Statements

Statements in Java can be grouped into various categories. Variable declarations with explicit initialization of the variables are called declaration statements (see Section 2.3, p. 31, and Section 4.1, p. 102). Other basic forms of statements are control flow statements (see Section 5.1, p. 158) and expression statements.

An expression statement is an expression terminated by a semicolon. The expression is evaluated for its side effect and its value discarded. Only certain types of expressions have meaning as statements. They include the following:

  • assignments (see Section 3.4, p. 47)

  • increment and decrement operators (see Section 3.7, p. 63)

  • method calls (see Section 3.17, p. 86)

  • object creation expression with the new operator (see Section 3.16, p. 84)

A solitary semicolon denotes the empty statement that does nothing.

A block, {}, is a compound statement which can be used to group zero or more local declarations and statements (see Section 4.5, p. 123). Blocks can be nested, since a block is a statement that can contain other statements. A block can be used in any context where a simple statement is permitted. The compound statement which is embodied in a block, begins at the left brace, {, and ends with a matching right brace, }. Such a block must not be confused with an array initialization block in declaration statements (see Section 4.1, p. 102).

Labeled statements are discussed in Section 5.4 on page 171.

Instance Methods and Object Reference this

Instance methods belong to every object of the class and can only be invoked on objects. All members defined in the class, both static and non-static, are accessible in the context of an instance method. The reason is that all instance methods are passed an implicit reference to the current object, that is, the object on which the method is being invoked. The current object can be referenced in the body of the instance method by the keyword this. In the body of the method, the this reference can be used like any other object reference to access members of the object. In fact, the keyword this can be used in any non-static context. The this reference can be used as a normal reference to reference the current object, but the reference cannot be modified.

The this reference to the current object is useful in situations where a local variable hides, or shadows, a field with the same name. In Example 4.4, the two parameters noOfWatts and indicator in the constructor of the Light class have the same names as the fields in the class. The example also declares a local variable location, which has the same name as one of the fields. The reference this can be used to distinguish the fields from the local variables. At (1), the this reference is used to identify the field noOfWatts, which is assigned the value of the parameter noOfWatts. Without the this reference at (2), the value of the parameter indicator is assigned back to this parameter and not to the field by the same name, resulting in a logical error. Similarly at (3), without the this reference it is the local variable location that is assigned the value of the parameter site, and not the field by the same name.

Example 4.4 Using this Reference
class Light {
    // Fields
    int     noOfWatts;      // wattage
    boolean indicator;      // on or off
    String  location;       // placement

    // Constructor
    public Light(int noOfWatts, boolean indicator, String site) {
        String location;

        this.noOfWatts = noOfWatts;   // (1) Assignment to field.
        indicator = indicator;        // (2) Assignment to parameter.
        location = site;              // (3) Assignment to local variable.
        this.superfluous();           // (4)
        superfluous();                // equivalent to call at (4)
    }

    public void superfluous() { System.out.println(this); }  // (5)

    public static void main(String[] args) {
        Light light = new Light(100, true, "loft");
        System.out.println("No. of watts: " + light.noOfWatts);
        System.out.println("Indicator: "    + light.indicator);
        System.out.println("Location: "     + light.location);
    }
}

Output from the program:

Light@df6ccd
Light@df6ccd
No. of watts: 100
Indicator: false
Location: null

If a member is not shadowed by a local declarations, then the simple name member is considered a short-hand notation for this.member. In particular, the this reference can be used explicitly to invoke other methods in the class. This is illustrated at (4) in Example 4.4, where the method superfluous() is called.

If, for some reason, a method needs to pass the current object to another method, it can do so using the this reference. This is illustrated at (5) in Example 4.4, where the current object is passed to the println() method.

Note that the this reference cannot be used in a static context, as static code is not executed in the context of any object.

Method Overloading

Each method has a signature, which comprises the name of the method and the types and order of the parameters in the formal parameter list. Several method implementations may have the same name, as long as the method signatures differ. This is called method overloading. Since overloaded methods have the same name, their parameter lists must be different.

Rather than inventing new method names, method overloading can be used when the same logical operation requires multiple implementations. The Java 2 SDK APIs make heavy use of method overloading. For example, the class java.lang.Math contains an overloaded method min(), which returns the minimum of two numeric values.

public static double min(double a, double b)
public static float min(float a, float b)
public static int min(int a, int b)
public static long min(long a, long b)

In the following examples, five implementations of the method methodA are shown:

void methodA(int a, double b) { /* ... */ }      // (1)
int  methodA(int a)           { return a; }      // (2)
int  methodA()                { return 1; }      // (3)
long methodA(double a, int b) { return b; }      // (4)
long methodA(int x, double y) { return x; }      // (5) Not OK.

The corresponding signatures of the five methods are as follows:

methodA(int, double)

1'

methodA(int)

2': Number of parameters.

methodA()

3': Number of parameters.

methodA(double, int)

4': Order of parameters.

methodA(int, double)

5': Same as 1'.

The first four implementations of the method named methodA are overloaded correctly, each time with a different parameter list and, therefore, different signatures. The declaration at (5) has the same signature methodA(int, double) as the declaration at (1) and is, therefore, not a valid overloading of this method.

void bake(Cake k)  { /* ... */ }                 // (1)
void bake(Pizza p) { /* ... */ }                 // (2)

int     halfIt(int a) { return a/2; }            // (3)
double  halfIt(int a) { return a/2.0; }          // (4) Not OK. Same signature.

The method named bake is correctly overloaded at (1) and (2), with two different signatures. In the implementation, changing just the return type (as shown at (3) and (4)), is not enough to overload a method, and will be flagged as a compile-time error. The parameter list in the definitions must be different. Overloaded methods should be considered as individual methods that just happen to have the same name. Methods with the same name are allowed since methods are identified by their signature. At compile time, the right implementation is chosen based on the signature of the method call. Details of method overloading resolution can be found in Section 6.2 on page 237. Only methods declared in the same class and those that are inherited by the class can be overloaded. Method overloading should not be confused with method overriding (see Section 6.2, p. 233).