Chapter 9: Methods (Functions)

In 1949, at the University of Cambridge, a machine called EDSAC ran its first program. It was built under Maurice Wilkes, it filled a room, and it read its instructions from punched paper tape. Very soon a problem appeared that had nothing to do with electronics. Every program needed the same small jobs done over and over — divide two numbers, work out a square root, print a result in readable form — and every programmer was writing those instructions again from scratch, making fresh mistakes each time.

A research student, David Wheeler, saw the waste. He worked out a way for a program to jump into a block of instructions stored elsewhere, run it, and then jump back to exactly the place it had left. The block did not have to know who had called it. The caller did not have to know how the block did its work. That technique is still known as the Wheeler jump, and it made something new possible: a library of ready-made blocks, kept on tape, that any program could borrow.

In 1951, Wilkes, Wheeler and Stanley Gill published a book describing how to build programs this way. Almost every idea in this chapter — calling, returning, handing values in — comes from that decision to stop rewriting and start reusing.

Diagram

Fig. 9.0-1 – a punched tape drawn as a long vertical strip of instructions on the left, labelled “main program”; a second, shorter strip on the right labelled “stored block of instructions”; an arrow leaving the left strip midway and entering the top of the right strip, labelled “call”; a second arrow leaving the bottom of the right strip and re-entering the left strip immediately below the point it left, labelled “return”; caption noting that the main program carries on from where it stopped

NoteCuriosity Corner

Q1. When a block of instructions finishes its work, how does the computer know which line to go back to? Q2. If you hand a number to a method and the method changes it, does your original number change as well? Q3. A method can send back only one answer. What do you do when the task has two answers to report? Q4. Some methods are called with an object written in front of them and some are not. What decides which? Q5. Can two methods in the same class be given exactly the same name, and if so, how does Java tell them apart?

By the end of this chapter you will be able to answer each of these questions for yourself.

9.1 Why a Program Needs Methods

Every program you have written so far has been a single stream of statements inside main. That works while a program is small. It stops working the moment the same piece of reasoning is needed twice, in two different places, on two different sets of data.

This section looks closely at what goes wrong when a program repeats itself, and at the idea that fixes it.

9.1.1 Repeated Work and the Cost of Repeating It

Suppose a class teacher wants the average of three subject marks printed for two students. The arithmetic is not hard: add the three marks, divide by three. The trouble is that the arithmetic has to happen twice.

Written directly inside main, it looks like this:

int p1 = 78, p2 = 85, p3 = 92;
int total1 = p1 + p2 + p3;
double avg1 = total1 / 3.0;
System.out.println("Meera: average = " + avg1);

int q1 = 64, q2 = 71, q3 = 69;
int total2 = q1 + q2 + q3;
double avg2 = total2 / 3.0;
System.out.println("Arjun: average = " + avg2);

Read the two halves side by side. They are the same three steps. Only the values differ, and the variable names had to be changed — total1 and total2, avg1 and avg2 — for no reason other than that both copies live in the same block.

What duplication actually costs

The obvious cost is length. The real costs are less obvious and much worse.

The first is correctness. Suppose you later decide the average should be rounded to one decimal place. You must remember that the calculation exists in two places and change both. Change one and forget the other, and the program is now quietly inconsistent — it still runs, still prints, and is still wrong for one of the two students.

The second is understanding. A reader of the code has to study both blocks carefully just to satisfy themselves that the two blocks really are the same. Nothing in the program says “these are one idea”; the reader has to discover it.

The third is growth. Two students become forty. Duplication is a solution that does not scale: the number of copies grows with the amount of data, which is exactly the wrong way round.

Q. Could a loop remove this duplication instead?

A. For forty students stored in an array, yes — a loop would repeat the same statements over different data. But a loop only helps when the repetitions sit next to each other and have the same shape. It cannot help when the same calculation is needed at scattered points in a program, or from inside two different tasks. A loop repeats statements; a method names them.

9.1.2 A Method as an Abstraction

The fix is to write the calculation once, give it a name, and refer to it by that name wherever it is needed. A named block of statements written inside a class and executed by naming it is called a method.

NoteDefinition

A method is a named block of statements, written inside a class, that performs one specific task; it may be given values to work on, and it may send one value back to the place from which it was named.

Naming the block does something more important than saving space. Once the calculation has a name — average — you can use it without holding its three steps in your head. You think about what it produces, not how it produces it. Hiding the details of a task behind a name that describes its purpose is called abstraction.

You already rely on this constantly. When you write Math.sqrt(49) you are not thinking about how a square root is computed; you are thinking “square root of 49”. That is exactly the relief a well-named method gives to a reader of your own program.

The statement that names a method and sets it running is a method call. The block that contains the call is the caller; the method is said to be called, or invoked, from it. In the figure below, one block of statements serves two calls made at two different points.

Diagram

Fig. 9.1-1 – left side: a tall box labelled “main” containing, in order, the lines “call average(78, 85, 92)”, “call average(64, 71, 69)”, “print both results”; right side: a single smaller box labelled “average” containing “add the three marks”, “divide by 3”, “send the result back”; two curved arrows run from the two call lines into the top of the average box, and two matching return arrows run back to the line immediately after each call; label the outgoing arrows “values go in” and the returning arrows “one value comes back”

Here is the same task written with a method.

average of three subject marks for two students, using one method Problem: For two students whose three subject marks are fixed in the program, compute and display each student’s average. The average calculation must be written once only.

class MarksReport
{
    public static double average(int a, int b, int c)
    {
        double avg;
        avg = (a + b + c) / 3.0;
        return avg;
    }

    public static void main(String args[])
    {
        double m, n;
        m = average(78, 85, 92);
        n = average(64, 71, 69);
        System.out.println("Meera: average = " + m);
        System.out.println("Arjun: average = " + n);
    }
}

Sample output:

Meera: average = 85.0
Arjun: average = 68.0

Variable description:

[Table:] Table 9.1 – variables of class MarksReport, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | a | int | first subject mark received by the method | | b | int | second subject mark received by the method | | c | int | third subject mark received by the method | | avg | double | average computed inside the method and sent back | | m | double | average returned for the first student | | n | double | average returned for the second student |

Notes: The division is written as / 3.0 rather than / 3 so that the arithmetic is done in double; with / 3 the three marks would be divided as integers and the fractional part thrown away. The method computes a value and hands it back rather than printing it, which leaves main free to decide how the result should be displayed. The word static in both headers is explained in Section 9.6; for the moment, read it as “this method can be named directly from main”.

What the rewrite bought

Compare the two versions on the three costs listed earlier. The calculation now exists once, so a change to it is a change in one place. A reader who trusts the name average can read main as a two-line summary of what the program does. And a third student, or a fortieth, adds one line rather than four.

Notice also that the method knows nothing about students, subjects or report cards. It takes three whole numbers and returns their average. That narrowness is a strength: the same method would serve equally well for three daily temperatures or three bus fares. A method that does one clearly stated thing is far easier to reuse than one that does a little of everything.

In older languages, and in a great deal of writing about programming, such a block is called a function or a subroutine. Java calls it a method because in Java it always belongs to a class rather than standing alone. The three words describe the same idea, and this book uses method throughout.

Key in MarksReport and run it. Now add a third student with marks 55, 60 and 68, using the existing method and without changing it in any way. How many new lines did you have to write? Work out on paper what the third line of output will read before you run the program again.

9.2 Defining and Invoking a Method

A method has two separate lives. It is defined once, inside a class, where its statements are written out; and it is invoked — possibly many times, possibly never — from wherever its work is needed. Defining a method does not run it. Nothing in a method executes until something calls it.

This section takes the definition apart piece by piece, then follows control as it moves from a caller into a method and back again.

9.2.1 The Method Header

The first line of a method definition is its method header. It is the line that tells the compiler, and any human reader, everything needed to use the method without reading its statements: who may call it, what it gives back, what it is called, and what it expects to be given.

A header has four parts, written in this order:

public static double average(int a, int b, int c)

Diagram

Fig. 9.2-1 – the header line public static double average(int a, int b, int c) printed large across the centre; four labelled arrows point up to it from below: “access specifier” to public, “modifier” to static, “return type” to double, “method name” to average; a fifth arrow points to the bracketed portion (int a, int b, int c) labelled “parameter list — three int values expected”; below the whole line, a brace spans it with the caption “method header”

Access specifier

The access specifier decides from where the method may be called. Java offers three specifier keywords, and a fourth possibility is to write no specifier at all.

Table 9.2 – access specifiers and the reach each one gives a method

Written as The method may be called from
public anywhere, including other classes
private only from inside the class that defines it
protected the class itself, other classes in the same package, and subclasses
nothing at all the class itself and other classes in the same package

Two of these deserve a warning. Writing nothing is not the same as writing public — a method with no specifier is unavailable outside its own package. And protected is bound up with inheritance, which is taken up in a later chapter; until then, the methods in this chapter are written public, so that they can be called from anywhere.

Modifier

A modifier changes some property of the method. Java has several, including static, final and abstract. Only static concerns us here, and it is the subject of Section 9.6. A method needs no modifier at all; the header is complete without one.

Return type

The return type is the type of the single value the method sends back to its caller. It is written immediately before the method name, and it may be any type — a primitive such as int or double, or a class type such as String.

A method that computes nothing to send back is given the return type void, which is a Java keyword meaning “no value comes back from here”. void is not a data type; you cannot declare a variable of type void.

Method name and parameter list

The method name follows the return type. Choose it to say what the method does: average, netPrice, isPrime. A name like calc or method1 tells a reader nothing, which defeats the whole purpose of naming a block in the first place.

Inside the brackets comes the parameter list: the values the method expects to be given, each written as a type followed by a name, separated by commas. Each one is declared individually — (int a, int b) is correct, (int a, b) is not. A method that needs no values is written with empty brackets, which are never left out.

Header, prototype and signature

Three words are used for closely related things, and they are easy to confuse.

The header is the whole first line, as written above. The same line, quoted on its own to describe a method without giving its statements, is often called a method prototype. The method signature is narrower: it is only the method name together with the types of its parameters, in order. For the header above, the signature is average(int, int, int).

The signature matters because it is what Java uses to decide which method a call refers to. Section 9.8 depends entirely on this.

9.2.2 The Method Body

Everything between the opening brace after the header and its matching closing brace is the method body. It holds ordinary statements — declarations, assignments, conditionals, loops — exactly as you would write them inside main.

Variables declared inside the body are local variables. They come into existence when the method is called and vanish when it finishes. Two consequences follow, and both are useful.

First, a local variable cannot be seen from outside the method, so a method can name its variables freely without any risk of clashing with names used elsewhere. Second, a local variable does not survive between calls: each call starts with fresh ones.

There is one rule with no exceptions. A local variable must be given a value before it is used. Java refuses to compile a method that reads a local variable which might still be uninitialised, and the message is variable x might not have been initialized.

9.2.3 The return Statement

The return statement ends a method and, in a method whose return type is not void, carries one value back to the caller. It is written return followed by an expression:

return a + b;

Four things are always true of it.

It terminates the method. Control leaves at that instant, whatever statements come after it — which is why writing a statement after an unconditional return is not merely useless but illegal.

It carries exactly one value. The type of that value must be compatible with the declared return type: a method declared int may not return a String.

A method may contain several return statements, at different exit points, but only one of them can ever be reached on a single call. The moment any of them executes, the method is over.

A method declared void may use return; on its own, with no expression, simply to leave early. It usually does not need to; reaching the closing brace ends a void method just as effectively.

NoteDefinition

The return statement terminates the execution of a method and, in a method with a non-void return type, sends exactly one value of a compatible type back to the point at which the method was called.

Here is a method with two exit points, both legal:

public static int larger(int x, int y)
{
    if (x > y)
        return x;
    else
        return y;
}

Whichever branch the condition takes, exactly one return runs and the method ends there.

writing a statement after return What students write:

public static int twice(int n)
{
    return 2 * n;
    System.out.println("done");
}

What happens: The program does not compile. The compiler reports unreachable statement on the println line, because control can never arrive there — the return above it always leaves the method first. The fix: Put the statement before the return, since return must be the last thing the method does on that path.

Q3. A method can send back only one answer. What do you do when the task has two answers to report?

A. A return statement carries exactly one value, and that limit is real — you cannot return two integers. There are two honest ways round it. The method can print both results instead of returning them, which suits a method whose job is display rather than calculation. Or the single value returned can itself be something that holds several values — an array, or an object with several fields. Section 9.4 shows why handing an array to a method is a particularly effective way of getting several answers back out of it.

9.2.4 Invoking a Method

A method is invoked by writing its name followed by brackets containing the values it is to work on. What happens next is a precise sequence, and it is worth stating exactly.

The caller’s execution pauses at the call. The values in the brackets are handed to the method’s parameters. Control jumps to the first statement of the method body and runs it through. When a return executes — or when a void method reaches its closing brace — control jumps back to the caller, arriving at the very point it left, carrying the returned value with it if there is one. The caller then continues.

Because the method returns a value, a call to a value-returning method is an expression. It can be used anywhere a value of that type could be used:

n = netPrice(250.0, 10);
System.out.println(netPrice(80.0, 5));
double total = netPrice(250.0, 10) + netPrice(80.0, 5);

A call to a void method cannot be used this way. It is a complete statement on its own, and assigning it to a variable is an error.

Q1. When a block of instructions finishes its work, how does the computer know which line to go back to?

A. Because the return address is recorded at the moment of the call. Before control jumps into the method, the position of the call is stored; when the method ends, that stored position is used to resume the caller at the statement immediately after the call. The method itself holds no record of who called it, which is exactly why the same method can be called from many different places and still return correctly to each of them.

The program below defines two methods of different shapes and calls each of them. Read main first: it is a summary of what the program does.

net price after discount, with a separate method for the printed rule Problem: A stationery shop lists an item at Rs 250.00 and allows a discount of 10 per cent. Compute the net price using a method that receives the listed price and the discount percentage and returns the amount payable. Print a line of dashes above and below the result using a second method.

class Bill
{
    public static double netPrice(double price, int percent)
    {
        double discount, net;
        discount = price * percent / 100.0;
        net = price - discount;
        return net;
    }

    public static void printRule()
    {
        System.out.println("--------------------");
    }

    public static void main(String args[])
    {
        double n;
        printRule();
        n = netPrice(250.0, 10);
        System.out.println("Net price = " + n);
        printRule();
    }
}

Sample output:

***
Net price = 225.0
***

Variable description:

[Table:] Table 9.3 – variables of class Bill, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | price | double | listed price received by netPrice | | percent | int | discount percentage received by netPrice | | discount | double | amount of discount, computed inside netPrice | | net | double | amount payable, returned by netPrice | | n | double | value returned by netPrice, held in main |

Notes: netPrice returns its answer instead of printing it, so the same method could be used to total several items. printRule returns nothing and takes nothing; its whole purpose is the side effect of printing. The division is written / 100.0 so that a discount such as 7 per cent of Rs 250.00 is not truncated to a whole number of rupees.

the order in which statements execute in class Bill

Step Statement executed Control is in Printed
1 printRule(); main —
2 System.out.println("--------------------"); printRule 20 dashes
3 closing brace reached, control returns back in main —
4 n = netPrice(250.0, 10); main, then jumps —
5 discount = price * percent / 100.0; (price 250.0, percent 10) netPrice —
6 net = price - discount; (discount 25.0, net 225.0) netPrice —
7 return net; carries 225.0 back back in main, n becomes 225.0 —
8 System.out.println("Net price = " + n); main Net price = 225.0
9 printRule(); and its println printRule, then back 20 dashes

Result: the output is the three lines shown above. The trace shows that the two calls to printRule run the same single statement twice, and that main resumes each time at the line following the call.

9.2.5 Forms of a Method Header

Two independent choices give a method its shape. Does it receive values? Does it send one back? Answering both gives four possible forms, and every method you write is one of them.

A method that receives values through a parameter list is a parameterised method; one with empty brackets is a non-parameterised method. A method with a return type other than void is a returnable method; one declared void is a non-returnable method.

Table 9.4 – returnable and non-returnable methods compared

Feature Returnable method Non-returnable method
Return type in header any type other than void void
Must contain a return value yes, on every path out no; return; alone is optional
Result reaches the caller as a value handed back not at all; the method acts on its own
Use at the call may be assigned, printed, or used in an expression may only be written as a complete statement
Suits calculation whose result the caller decides how to use display, or a change the caller does not need reported

The two methods of class Bill illustrate two of the four forms: netPrice is parameterised and returnable, printRule is non-parameterised and non-returnable. The remaining two look like this:

public static void showFare(int km)
{
    System.out.println("Fare = " + (8 + 3 * km));
}

That one is parameterised and non-returnable: it is given a distance and prints the fare itself, deciding the wording of the output on the caller’s behalf.

public static int seatsPerRow()
{
    return 4;
}

That one is non-parameterised and returnable: it needs nothing from the caller and reports a fixed value.

Which form to choose is a design decision, not a matter of taste. Ask what the caller will want to do with the result. A method that prints its own answer has decided the format for every future caller; a method that returns the answer leaves that choice open, and can be used inside a larger calculation. As a working rule, prefer to return a value, and print only when printing is genuinely the method’s purpose.

Q. A method is declared void but the programmer writes n = display(5);. What does the compiler say?

A. It refuses to compile, because there is no value on the right-hand side to assign — void means nothing comes back. The message names the mismatch of types. The correct call is simply display(5); on a line of its own.

Change printRule so that it takes an int and prints that many dashes, then call it as printRule(30); from main. Which of the four forms is it now? Predict the exact output before running the program.

9.3 Formal Arguments and Actual Arguments

A method that always works on the same values is of very little use. What makes average worth writing is that it will average any three whole numbers you give it. The mechanism that lets you give them is the subject of this section, and it has two ends: the names written in the method header, and the values written at the call.

The variables declared in a method header, which receive values when the method is called, are its formal arguments. The values written inside the brackets at the point of call are the actual arguments.

public static double interest(double p, double r, double t)   // p, r, t are formal arguments
si = interest(amount, rate, years);                        // amount, rate, years are actual arguments

The two lists face each other across the call. Everything in this section follows from how they are matched up.

Table 9.5 – actual arguments and formal arguments compared

Feature Actual arguments Formal arguments
Where written inside the brackets of the method call inside the brackets of the method header
What they are values, or variables and expressions that produce values variable declarations, each with a type
Belong to the calling block the method being called
Exist when already exist before the call come into existence when the call is made
Written with a type no yes, one type per argument

Most writing about Java calls these actual parameters and formal parameters, and some writers reserve “argument” for the value and “parameter” for the declaration. The two pairs of words mean the same things here, and you will meet both.

9.3.1 How Values Reach the Formal Arguments

At the moment a method is called, three things must line up between the two lists.

The number must match. A method with three formal arguments must be called with exactly three actual arguments; two or four will not compile.

The order decides which value goes where. The first actual argument is given to the first formal argument, the second to the second, and so on. Position is everything — names count for nothing in this matching.

The types must be compatible. An int value may be given to a double formal argument, because Java widens the value automatically; this is automatic type coercion. The reverse is not allowed: a double value handed to an int formal argument is refused, because the fractional part would be lost. The compiler reports that a double cannot be converted to int without an explicit cast.

Once matched, each actual argument is copied into its formal argument. The formal argument then behaves exactly like a local variable of the method that happens to start with a useful value.

Diagram

Fig. 9.3-1 – upper half labelled “main”, showing three boxes side by side: amount holding 12000.0, rate holding 7.5, years holding 3; lower half labelled “interest”, showing three empty boxes p, r, t in the same order; three straight downward arrows join amount→p, rate→r, years→t, each arrow labelled “copy”; a note beside the arrows reads “matched by position, not by name”

The program below uses this deliberately: not one actual argument shares a name with the formal argument that receives it.

simple interest computed by a method Problem: For a principal of Rs 12000.00 invested at 7.5 per cent per year for 3 years, compute the simple interest using a method that receives the principal, the rate and the time, and returns the interest.

class SimpleInterest
{
    public static double interest(double p, double r, double t)
    {
        double si;
        si = p * r * t / 100.0;
        return si;
    }

    public static void main(String args[])
    {
        double amount = 12000.0, rate = 7.5;
        int years = 3;
        double result = interest(amount, rate, years);
        System.out.println("Principal = " + amount);
        System.out.println("Interest = " + result);
    }
}

Sample output:

Principal = 12000.0
Interest = 2700.0

Variable description:

[Table:] Table 9.6 – variables of class SimpleInterest, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | p | double | principal, received by the method | | r | double | rate per cent per year, received by the method | | t | double | time in years, received by the method | | si | double | interest computed inside the method and returned | | amount | double | principal held in main | | rate | double | rate held in main | | years | int | time held in main | | result | double | value returned by interest, held in main |

Notes: years is an int and is received by the double formal argument t, so its value is widened to 3.0 during the copy; nothing is lost. Time is declared double rather than int because a deposit may well be held for two and a half years. Writing / 100.0 rather than / 100 keeps the whole expression in double, which matters as soon as the rate is not a whole number.

actual arguments given in the wrong order What students write:

public static void percentage(int obtained, int total)
{
    System.out.println(obtained * 100 / total + " %");
}
// called from main as:
percentage(500, 372);

What happens: The program compiles perfectly and runs without complaint, because both arguments are int and the count is right. It simply prints the wrong figure — 134 % for a student who scored 372 out of 500. Nothing warns you. The fix: Call it as percentage(372, 500);. Order is the only thing that decides which value lands in which formal argument, so a method’s arguments should be named in the header so plainly that a mis-ordered call looks wrong on the page.

Q. Must an actual argument have the same name as the formal argument it feeds?

A. No, and it usually should not. The names live in different blocks and never meet. Giving them the same name is harmless but slightly misleading, because it suggests a connection that does not exist — the value is copied across, and the two variables remain entirely separate afterwards.

9.3.2 Scope and Lifetime of Arguments and Local Variables

The region of a program in which a name can be used is its scope. A formal argument, like any local variable of the method, has the method body as its scope and nothing more.

This cuts both ways, and both directions surprise students at first.

A method cannot see the caller’s variables. Inside interest, the names amount, rate and years mean nothing at all; writing them there produces the compile error cannot find symbol. The method knows only the values it was handed, under the names it gave them.

The caller cannot see the method’s variables either. After the call, si does not exist in main — the only thing that crossed back is the value returned.

The lifetime of a formal argument is a single call. It is created when the method is entered and destroyed when the method ends. Call the method again and a fresh set is created, with no memory of the previous call.

That is what makes a method safe to reuse. Because each call works on its own private copies, two calls cannot interfere with each other, however many times the method is called and from wherever it is called.

Q. If a method needs a value that was not passed to it, can it simply read the variable from main?

A. No. A local variable of main is invisible to every other method. If a method needs a value, that value must be passed as an actual argument — which is a discipline worth welcoming, because it means a method’s header states its complete list of requirements.

Add the line System.out.println(amount); as the first statement inside the method interest and try to compile the class. Note the exact error the compiler gives. Then remove the line, and instead print p at the same place. Explain in one sentence why one of the two compiles and the other does not.

9.4 Different Behaviour of Primitive and Object Arguments

Section 9.3 said that an actual argument is copied into its formal argument. That single sentence explains everything in this section — but only once you are clear about what is copied.

For a primitive value, the variable holds the value itself, so the copy is a copy of the value. For an array or an object, the variable does not hold the object; it holds a reference, which is the location at which the object is stored. Copying such a variable copies the reference, not the object it leads to.

NoteDefinition

A reference is a value that identifies where an object is stored, held in a variable of a class or array type; the variable does not contain the object itself.

The consequence is the whole of this section: two references can lead to one object, and a change made through either of them is visible through the other.

9.4.1 Passing a Primitive Value

When a primitive is passed, the method receives a private copy. The formal argument and the actual argument are two separate variables in two separate places, holding equal values at the moment of the call and independent from then on. This is what is traditionally called pass by value.

a method that doubles a primitive argument Problem: Pass an int to a method that doubles it and prints the doubled figure. Print the caller’s variable after the call, and observe whether it has changed.

class PrimitiveArg
{
    public static void doubleIt(int n)
    {
        n = n * 2;
        System.out.println("Inside the method, n = " + n);
    }

    public static void main(String args[])
    {
        int count = 25;
        doubleIt(count);
        System.out.println("Back in main, count = " + count);
    }
}

Sample output:

Inside the method, n = 50
Back in main, count = 25

Variable description:

[Table:] Table 9.7 – variables of class PrimitiveArg, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | n | int | formal argument of doubleIt, holding a copy of the value passed | | count | int | value held in main and passed to the method |

Notes: The method changes n and nothing else. count is untouched because n was never count — it was a second variable that happened to start with the same value.

Diagram

Fig. 9.4-1 – two labelled regions drawn one above the other. Upper region labelled “main” contains one box named count holding 25. Lower region labelled “doubleIt” contains one box named n holding 50. A single arrow from count to n is labelled “copy of the value, made at the call”; a caption beneath reads “two boxes, two values — changing one cannot change the other”

9.4.2 Passing a Reference to an Array or Object

Now pass an array. The copying rule does not change: the actual argument is still copied into the formal argument. But the value being copied is a reference, so after the copy there are two references and still only one array.

The method can therefore reach into the caller’s array and change its elements, and those changes remain after the method ends. This is what is traditionally called pass by reference, and in Java it applies to every array and every object.

adding a delivery quantity to every item of a stock list Problem: A shop keeps the stock of four items in an array. Pass the array to a method that adds a fixed delivery quantity to every element. Display the array before and after the call to see whether the change survives.

class ArrayArg
{
    public static void addStock(int list[], int extra)
    {
        for (int i = 0; i < list.length; i++)
            list[i] = list[i] + extra;
    }

    public static void show(int list[])
    {
        System.out.print(list[0]);
        for (int i = 1; i < list.length; i++)
            System.out.print(", " + list[i]);
        System.out.println();
    }

    public static void main(String args[])
    {
        int stock[] = {12, 7, 20, 5};
        show(stock);
        addStock(stock, 10);
        show(stock);
    }
}

Sample output:

12, 7, 20, 5
22, 17, 30, 15

Variable description:

[Table:] Table 9.8 – variables of class ArrayArg, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | list | int[] | formal argument holding a copy of the reference to the caller’s array | | extra | int | delivery quantity added to every element | | i | int | loop counter, local to each method | | stock | int[] | reference to the array of four stock figures, held in main |

Notes: addStock returns nothing, yet its work survives the call, because it altered the array itself rather than any local variable. show prints the first element before the loop so that the commas fall between the numbers and not after the last one; written this way it assumes the array has at least one element, which is a real limitation of the method and would matter if it were ever called with an empty array.

Diagram

Fig. 9.4-2 – upper region labelled “main” with one small box named stock containing a dot; lower region labelled “addStock” with one small box named list also containing a dot; to the right, a single row of four cells labelled with index numbers 0 to 3 holding 12, 7, 20, 5, drawn once only and labelled “one array”; arrows run from the dot in stock and from the dot in list to the same row of cells; caption reads “two references, one array — a change through either is seen through the other”

addStock on the array {12, 7, 20, 5} with extra = 10

i list[i] before list[i] after array now
0 12 22 22, 7, 20, 5
1 7 17 22, 17, 20, 5
2 20 30 22, 17, 30, 5
3 5 15 22, 17, 30, 15

Result: the loop ends when i reaches 4, and main finds the array in its final state. The trace shows that no value was ever copied back at the end — the elements were changed where they stood.

The one thing the method still cannot do

The method can change the contents of the array. It cannot make the caller’s variable refer to a different array. Consider:

public static void replace(int list[])
{
    list = new int[]{1, 2, 3};
}

Here the formal argument list is made to refer to a brand-new array. But list is a local variable, and reassigning it changes only where it points. The caller’s variable goes on referring to the original array, which is untouched. Call replace(stock) and stock still holds 22, 17, 30, 15.

This is worth stating plainly, because it is the point at which the traditional name becomes misleading. What crosses into a method is always a copy — of a value, or of a reference. Changing what the copy leads to affects the caller; changing what the copy is does not.

Table 9.9 – passing a primitive compared with passing an array or object

Feature Primitive argument Array or object argument
What is copied at the call the value itself the reference to the object
Number of copies of the data two — one per method one; both references lead to it
Change made to the contents inside the method invisible to the caller visible to the caller after the call
Formal argument reassigned inside the method no effect on the caller no effect on the caller
Traditional name pass by value pass by reference
Applies to byte, int, short, long, float, double, boolean, char every array and every object

Q2. If you hand a number to a method and the method changes it, does your original number change as well?

A. If you hand it a plain number — an int, a double, any primitive — then no. The method works on its own copy, and your variable is untouched, as PrimitiveArg shows. If you hand it an array or an object, then yes: the method receives a reference to the very same object, so any change it makes to the contents is still there when the method ends. The deciding factor is not what the method does, but what kind of thing you passed it.

9.4.3 Why a Wrapper Argument Behaves Like a Primitive

An Integer is an object, and a String is an object. By the rule just established, passing either should let the method change it in a way the caller can see. In practice it never does, and the reason is worth understanding rather than memorising.

Both classes are immutable: once such an object exists, there is no operation anywhere that alters its contents. Every operation that appears to change one — adding to an Integer, joining text to a String — actually builds a new object and leaves the original exactly as it was.

NoteDefinition

An immutable object is one whose contents cannot be altered after it has been created; any operation that appears to modify it produces a new object instead.

So a method given an immutable object has a reference to something it cannot change, and the only thing it can do is point its own formal argument at the new object it builds — which, as Section 9.4.2 established, the caller never sees.

passing an Integer and a String to a method Problem: Pass an Integer and a String to a method that adds to each of them, and print both inside the method and again in the caller, to see which changes survive.

class WrapperArg
{
    public static void grow(Integer boxed, String label)
    {
        boxed = boxed + 100;
        label = label + " Extension";
        System.out.println("Inside the method: " + boxed + ", " + label);
    }

    public static void main(String args[])
    {
        Integer count = 40;
        String name = "Station Road";
        grow(count, name);
        System.out.println("Back in main: " + count + ", " + name);
    }
}

Sample output:

Inside the method: 140, Station Road Extension
Back in main: 40, Station Road

Variable description:

[Table:] Table 9.10 – variables of class WrapperArg, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | boxed | Integer | formal argument, made to refer to a new Integer inside the method | | label | String | formal argument, made to refer to a new String inside the method | | count | Integer | wrapper object held in main | | name | String | text held in main |

Notes: boxed + 100 unwraps the object to an int, adds 100, and wraps the result in a new object; boxed is then made to refer to that new object. The same happens to label. Neither original object was altered, so main prints what it always held. Assigning 40 directly to an Integer variable relies on automatic wrapping, available from Java 5.0 onwards.

Q. Then how does a method ever change a String for its caller?

A. It does not change the String — it returns a new one, and the caller assigns it: name = expand(name);. Where text really must be modified in place, Java provides StringBuffer, whose contents can be altered; a StringBuffer passed to a method behaves like the array in Section 9.4.2.

Change addStock so that its first statement is list = new int[4]; and run ArrayArg again. Predict the two printed lines before you run it, then explain in one sentence why the array in main is or is not affected.

9.5 Pure and Impure Methods

Section 9.4 established what a method is able to reach. This section asks a different question, and it is a question about design rather than mechanism: of the things a method could disturb, which ones should it disturb?

Two methods may both return exactly the right answer and still differ completely in what they leave behind. One hands back its result and leaves every piece of data exactly as it found it. The other hands back the same result, having quietly rearranged the caller’s data on the way. Both compile. Both print the expected figure the first time you run them. Only one of them can be trusted the second time.

The state of an object

The data held by an object at a given moment is called its state. For an array of marks, the state is simply the five numbers currently stored in it. When a method alters any of those numbers, the state has changed, and the change outlives the call.

A method that leaves the state alone is a pure method. Because such a method only reads data and reports on it, it is also called an accessor.

NoteDefinition

A pure method returns a value and does not change the state of any object — neither an object passed to it nor any other object it can reach. Calling it leaves all data exactly as it was.

A method that alters the state of an object during its call is an impure method, also called a mutator. An impure method may return a value or return nothing at all; what makes it impure is the change it leaves behind, not what it hands back.

NoteDefinition

An impure method changes the state of an object during its call. The change remains in effect after the method has ended, and is visible to the caller.

The unwanted change an impure method leaves behind is often called a side effect — “side” because it is not the answer the caller asked for, and effect because it happens anyway.

Why a primitive argument can never make a method impure

The link with Section 9.4 is exact and worth stating once, plainly.

A method can only alter data it can reach. A primitive argument gives it a private copy, so nothing the method does to that copy can touch the caller. A method that receives only primitives and returns a value is therefore pure whether its author intended it or not.

Impurity requires a reference. It is the array or the object handed across that gives the method a route back into the caller’s data — the same route that made addStock useful in Section 9.4.2 now becomes the route by which a careless method does damage.

The same method, two different answers

Here are both kinds together. A school moderates its marks: no mark may be recorded above a ceiling of 90, and any mark above it is brought down to 90. One method totals the marks; another applies the moderation.

totalling marks before and after moderation Problem: Five marks are stored in an array. Display the total. Then apply a ceiling of 90 to the marks, reporting how many marks had to be brought down, and display the total again.

class Moderation
{
    public static int total(int m[])
    {
        int i, sum = 0;
        for (i = 0; i < m.length; i++)
            sum = sum + m[i];
        return sum;
    }

    public static int moderate(int m[], int limit)
    {
        int i, changed = 0;
        for (i = 0; i < m.length; i++)
        {
            if (m[i] > limit)
            {
                m[i] = limit;
                changed = changed + 1;
            }
        }
        return changed;
    }

    public static void main(String args[])
    {
        int marks[] = {88, 62, 95, 71, 90};
        int capped;
        System.out.println("Total before = " + total(marks));
        capped = moderate(marks, 90);
        System.out.println("Marks brought down = " + capped);
        System.out.println("Total after = " + total(marks));
    }
}

Sample output:

Total before = 406
Marks brought down = 1
Total after = 401

Variable description:

[Table:] Table 9.11 – variables of class Moderation, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | m | int[] | formal argument of both methods, referring to the caller’s array | | limit | int | ceiling above which a mark is brought down | | i | int | loop counter, local to each method | | sum | int | running total accumulated inside total | | changed | int | count of marks brought down, returned by moderate | | marks | int[] | reference to the five marks, held in main | | capped | int | value returned by moderate, held in main |

Notes: total reads every element and writes to none, so it is pure. moderate writes into the caller’s array, so it is impure — and its impurity is the whole point of the method, since a moderation that did not survive the call would be useless. The single mark above the ceiling is 95, which becomes 90, and the total accordingly falls by 5.

Read the output once more, because it contains something that ought to look strange. The method total was called twice, with the same argument, and gave two different answers. Nothing in total changed between the calls; nothing in the array literal changed. What changed was the state of the array, and it was changed by the call sitting between them.

Diagram

Fig. 9.5-1 – two rows of five cells each, drawn one above the other. Upper row labelled “the array before the call to moderate” holding 88, 62, 95, 71, 90 with index numbers 0 to 4 written above the cells. Lower row labelled “the same array after the call” holding 88, 62, 90, 71, 90 with the third cell shaded and marked “95 brought down to 90”. A downward arrow between the rows is labelled “moderate(marks, 90)”; a note to the right reads “one array, altered where it stands — no copy was made”

That is not a bug here. It is exactly what the school asked for. But it is the reason an impure method must be recognised as impure and used deliberately, because a reader of main who assumed both totals must agree would have no way of finding the fault by looking at total.

Table 9.12 – pure and impure methods compared

Feature Pure method Impure method
Effect on the state of an object none; data is left as it was found the state is altered, and the change survives the call
Other name accessor mutator
Return value returns a value; that is its whole output may return a value or be declared void
Called twice with the same data gives the same answer every time may give a different answer each time
Reaches the caller’s data through nothing; it only reads what it is given a reference to an array or object
Typical use computing, searching, testing, reporting sorting, updating, correcting, filling
Chief risk none of this kind a caller unaware of the change works on data that has moved under it

The row worth memorising is the fourth. A pure method called twice with the same data must give the same answer, and this is what makes it safe to use inside a larger expression, safe to test on its own, and safe to call as often as you like. The moment a method is impure, the order of calls begins to matter.

expecting a pure library method to change its object What students write:

String city = "jaipur";
city.toUpperCase();
System.out.println(city);

What happens: The program compiles, runs, and prints jaipur in lower case. toUpperCase() is a pure method: it builds and returns a new String in upper case and leaves the original exactly as it was. The returned object is then thrown away, because nothing was done with it. The fix: Assign the returned value — city = city.toUpperCase();. Nearly every method of the String class works this way, and reading them as pure methods explains all of them at once.

Q. Is a method that only displays something an impure method?

A. No. Displaying a value alters no object’s state, so such a method is not a mutator. Nor is it what is usually meant by a pure method, since a pure method is expected to return the value it has computed. The test to remember is about data: purity asks whether the method left the data as it found it.

Rewrite moderate so that it leaves the caller’s array untouched. Decide what it must return instead, and what main must then do with the returned value. Run both versions and confirm that in your new version the two printed totals agree.

9.6 Static Methods and Static Variables

Every method written so far in this chapter has carried the word static in its header, and every call has been made by writing the method’s name on its own. Both facts were left unexplained, with a promise that this section would take them up.

The word static settles a question that has to be answered about every method and every variable in a class: does this thing belong to the class itself, or does it belong to each object made from the class? A class is a description; an object is a particular thing made to that description. A class Student describes what any student record holds; each individual record is an object. Some information belongs to the individual — a roll number, a set of marks. Other information belongs to the whole class of them — the name of the school, or the number of records created so far. Java lets you say which is which, and static is how you say it.

9.6.1 Static Variables

A variable declared inside a class, but outside every method, is a member of that class. There are two kinds.

An instance variable is declared without the word static. Every object gets its own copy, and the copies are independent: change one object’s copy and no other object notices.

A static variable, declared with the word static, is quite different. There is exactly one copy of it, belonging to the class itself, and every object made from that class shares that single copy. It exists even before any object has been created, and it goes on existing when none is left. For this reason it is also called a class variable.

NoteDefinition

An instance variable is a class member of which every object holds its own separate copy.

NoteDefinition

A static variable is a class member of which exactly one copy exists, belonging to the class and shared by every object made from it, whether or not any object has been created.

Fig. 9.6-1 shows the arrangement for a class with one variable of each kind.

Diagram

Fig. 9.6-1 – a large outer rectangle labelled “class Student” containing, at its top, one small box labelled “static int admitted” holding the value 3, marked “one copy, belongs to the class”. Below it, inside the same outer rectangle, three separate smaller rectangles labelled “object 1”, “object 2”, “object 3”, each containing its own box labelled “int roll” holding 101, 102 and 103 respectively, marked “one copy per object”. Three arrows run from the three objects to the single static box, labelled “all three share this one”

A variable that remembers between calls

Section 9.3.2 established that a local variable dies when its method ends, so each call starts afresh. A static variable behaves in the opposite way, and that is precisely what makes it useful. It belongs to the class, not to the call, so a value written into it by one call is still there when the next call arrives.

A ticket counter at a school fair shows this at its simplest. Every ticket must carry a number one higher than the last, which means the counter has to remember something between calls.

issuing serially numbered tickets with a static counter Problem: Issue three tickets, each carrying a number one greater than the previous one, and then display how many tickets have been issued in all.

class TicketCounter
{
    static int issued = 0;

    public static int issueTicket()
    {
        issued = issued + 1;
        return issued;
    }

    public static void main(String args[])
    {
        System.out.println("Ticket number " + issueTicket());
        System.out.println("Ticket number " + issueTicket());
        System.out.println("Ticket number " + issueTicket());
        System.out.println("Tickets issued so far = " + issued);
    }
}

Sample output:

Ticket number 1
Ticket number 2
Ticket number 3
Tickets issued so far = 3

Variable description:

[Table:] Table 9.13 – variables of class TicketCounter, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | issued | int | static variable holding the number of tickets issued so far; survives between calls |

Notes: issued is declared outside every method, so it is a class member rather than a local variable, and it is declared static, so there is one of it for the whole class. It is initialised to 0 once, when the class is first loaded, and never again — which is why the three calls see 0, then 1, then 2 on entry.

three successive calls to issueTicket

Call issued on entry issued after the increment Value returned Printed
1 0 1 1 Ticket number 1
2 1 2 2 Ticket number 2
3 2 3 3 Ticket number 3

Result: after the third call issued holds 3, which main prints directly. The trace shows the one thing a local variable could never do — carry a value from the end of one call to the start of the next.

Had issued been declared inside issueTicket instead, it would have been created and set to 0 afresh on every call, and all three tickets would have been numbered 1. The choice between a local variable and a static variable is therefore a choice about memory in the ordinary English sense: should this value be forgotten when the method ends, or remembered?

The other common use of a static variable is a value shared by everything in the class because it is the same for all of them — a rate of interest, a maximum capacity, a free period of 14 days. Where such a value must never change once set, it is declared static final, and the compiler then refuses any attempt to assign to it a second time.

9.6.2 Static Methods

A static method is one declared with the word static, and it is declared so for the same reason a variable is: it belongs to the class rather than to any one object.

NoteDefinition

A static method is a method that belongs to the class itself. It can be called without creating any object of the class, and it can directly use only the static members of its class.

The second half of that definition is not an arbitrary rule; it follows from the first half. A static method may be running at a time when no object exists at all. If it were to mention an instance variable, there would be no honest answer to the question which object’s copy? Java therefore refuses the attempt at compile time rather than letting it fail later.

So a static method works only on what it is given through its arguments and on the static members of its class. Every method in this chapter so far has been of this kind, which is why every one of them took all the data it needed as arguments.

Q. Why must main itself be declared static?

A. Because it is the first method to run. When a program is started, no object exists yet — the program has not run long enough to create one. If main needed an object, nothing could ever get started. Declaring it static lets Java call it on the class alone.

You have been using someone else’s static methods for a long time without noticing. Math.sqrt is called by writing the class name and the method name, and nobody ever creates a Math object. That is the mark of a static method: the class name is enough.

9.6.3 Calling Static and Non-static Methods

A method declared without the word static is a non-static method, also called an instance method. It belongs to each object, and it may freely use that object’s instance variables. Since it works on an object’s data, it cannot be called until an object exists.

Creating one is a single statement — Distance trip = new Distance(); — after which the method is called by writing the object’s name, a dot, and the method’s name.

The class below contains one method of each kind, computing the same bus fare in the two different ways.

a bus fare computed by a static method and by an instance method Problem: A city bus charges a fixed 8 rupees plus 3 rupees for every kilometre travelled. Compute the fare for a journey of 6 kilometres twice: once with a static method that is given the distance, and once with an instance method that reads the distance stored in an object.

class Distance
{
    int km;

    public static int fareFor(int d)
    {
        return 8 + 3 * d;
    }

    public int fare()
    {
        return 8 + 3 * km;
    }

    public static void main(String args[])
    {
        System.out.println("Static call  : " + fareFor(6));
        Distance trip = new Distance();
        trip.km = 6;
        System.out.println("Instance call: " + trip.fare());
    }
}

Sample output:

Static call  : 26
Instance call: 26

Variable description:

[Table:] Table 9.14 – variables of class Distance, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | km | int | instance variable holding the distance stored in one object | | d | int | formal argument of the static method, receiving a distance | | trip | Distance | reference to the object created in main |

Notes: The two methods return the same number by different routes. fareFor knows nothing until it is told, so its data arrives as an argument. fare needs no argument, because the distance it works on is already inside the object it belongs to — writing km inside fare means this object’s km. That is why the call must name an object: trip.fare() says which object’s km to read, and fare() on its own would not say.

Table 9.15 – static and non-static methods compared

Feature Static method Non-static (instance) method
Declared with the word static in the header no static in the header
Belongs to the class itself each object of the class
Object needed before calling no yes; one must be created first
Written at the call as the method name alone, or class name and dot object name, dot, method name
May directly use static members only both instance members and static members
Gets its data from its arguments and static variables its arguments and the object’s own instance variables
Suits a calculation that depends only on what it is given an operation on the data held by one particular object

Q4. Some methods are called with an object written in front of them and some are not. What decides which?

A. The word static in the method’s header. A static method belongs to the class, so the class alone is enough to call it and no object is written in front. A non-static method belongs to an object and works on that object’s own data, so the call must name the object it is to act on — trip.fare(). The dot is not decoration; it answers the question the method could not otherwise answer, namely whose data to use.

calling an instance method from main without an object What students write:

    public static void main(String args[])
    {
        System.out.println(fare());
    }

What happens: The program does not compile. main is static and fare is not, so at the moment main runs there may be no object at all, and the call has no data to work on. The compiler reports that a non-static method cannot be referenced from a static context. The fix: Either create an object and call the method on it, as Distance trip = new Distance(); trip.fare();, or — if the method genuinely does not need any object’s data — declare the method static and pass it what it needs. Both are correct; which one is right depends on whether the data belongs to an object or to the caller.

In TicketCounter, move the declaration of issued inside issueTicket so that it becomes a local variable. Predict the four lines of output before compiling, then run it and account for any line that surprised you.

9.7 The this Operator

Section 9.6 left one question hanging. Inside a non-static method, writing the name of an instance variable means this object’s copy of it. But how does the method know which object that is? It was never told. No argument carried the information, and the same method serves every object of the class.

The answer is that it was told, silently. Every non-static method receives, along with its declared arguments, a reference to the object on which it was called. That object is known as the current object, and the keyword this is the name by which the method may refer to it.

NoteDefinition

Inside a non-static method, this is a reference to the object on which that method was called — the current object. Writing this.x means the instance variable x belonging to that particular object.

So trip.fare() and front.fill(120) do more than name a method. They also decide, for the duration of that call, what this refers to. Call the same method on a different object and this refers to that one instead. Fig. 9.7-1 shows the arrangement.

Diagram

Fig. 9.7-1 – two rectangles side by side, one labelled “object front” containing a box “litres = 165”, the other labelled “object roof” containing a box “litres = 500”. Below them, a single rectangle labelled “the method fill, written once” containing the line “this.litres = this.litres + litres”. Two dashed arrows run upward from the word “this” in that line, one to each object, each labelled with the call that makes it point there: “during front.fill(45)” on the left arrow and “during roof.fill(500)” on the right arrow. A caption reads “one method, two objects — this refers to whichever object was named at the call”

Where the keyword becomes necessary

Most of the time this may be left out. Writing litres inside a non-static method already means this.litres, and Java supplies the missing part for you.

There is one situation where it cannot be left out, and it is common enough to be worth learning properly. When a formal argument is given the same name as an instance variable, the argument wins inside the method. Its declaration is nearer, so for the length of that method the name refers to the argument and the instance variable becomes unreachable by that name. This hiding of one name by a nearer declaration of the same name is called shadowing.

Naming the argument something different would avoid the clash. But the natural name for the argument is very often the natural name for the variable — a method that fills a tank with a quantity of litres wants to call that quantity litres — and inventing litres2 or l to dodge the problem makes the header worse. The keyword this lets you keep the good name and still say precisely which of the two you mean.

filling two water tanks, using this to separate the argument from the instance variable Problem: A school has two water tanks. Each can be filled repeatedly, and each keeps its own level. Fill the front tank with 120 litres and then a further 45, fill the roof tank with 500, and display the level of each.

class WaterTank
{
    int litres;

    public void fill(int litres)
    {
        this.litres = this.litres + litres;
    }

    public int level()
    {
        return litres;
    }

    public static void main(String args[])
    {
        WaterTank front = new WaterTank();
        WaterTank roof = new WaterTank();
        front.fill(120);
        roof.fill(500);
        front.fill(45);
        System.out.println("Front tank = " + front.level());
        System.out.println("Roof tank  = " + roof.level());
    }
}

Sample output:

Front tank = 165
Roof tank  = 500

Variable description:

[Table:] Table 9.16 – variables of class WaterTank, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | litres (instance variable) | int | water currently held by one tank | | litres (formal argument of fill) | int | quantity being poured in during this call | | front | WaterTank | reference to the tank on the ground | | roof | WaterTank | reference to the tank on the roof |

Notes: Inside fill, the plain name litres is the argument and this.litres is the tank’s own level, so the assignment reads exactly as the English does: this tank’s level becomes this tank’s level plus the quantity poured in. Inside level there is no argument to clash with, so the plain name is enough and this is left out. Neither tank was given a starting level, and none was needed: an instance variable is set to a default value when its object is created, and for an int that default is 0. Local variables receive no such courtesy, as Section 9.2.2 noted.

the three calls to fill, showing what this refers to in each

Call this refers to this.litres on entry Argument litres this.litres after
front.fill(120) the front tank 0 120 120
roof.fill(500) the roof tank 0 500 500
front.fill(45) the front tank 120 45 165

Result: the front tank ends at 165 and the roof tank at 500. The trace shows that the middle call changed nothing in the front tank, because this referred to a different object for the length of that call.

leaving out this where the names clash What students write:

    public void fill(int litres)
    {
        litres = litres + litres;
    }

What happens: The program compiles and runs, and every tank reports a level of 0. Both names on that line refer to the argument, so the statement doubles the argument and then throws the result away when the method ends. The tank’s own litres was never mentioned, and so was never changed. Nothing in the output points at the guilty line. The fix: Write this.litres = this.litres + litres;. The rule to carry away is that where an argument shares a name with an instance variable, every mention of the instance variable inside that method needs this.

Q. Can this be used inside a static method?

A. No, and the reason follows from Section 9.6. A static method belongs to the class and may run when no object exists at all, so there is no current object for this to refer to. Using it there is a compile-time error, not a risk taken at run time.

this has one further use worth knowing, although this chapter does not develop it. Because it is a reference to the current object, it can be handed on wherever a reference is wanted — passed to another method, or returned — which is how an object arranges to be acted upon by code written elsewhere.

Add a method public void empty() to WaterTank that sets the tank’s level back to 0, and call it on the roof tank only. Predict both printed lines before you run the program, and say whether empty is a pure method or an impure one.

9.8 Method Overloading

Some operations are genuinely one operation carried out on different kinds of data. Finding an area is finding an area, whether the shape is a square described by one measurement, a rectangle described by two, or a triangle described by a base and a height.

A language could insist on a different name for each — areaOfSquare, areaOfRectangle, areaOfTriangle. That works, and it is what you would have to do if Java forbade repeated names. But it forces the caller to remember three names for one idea, and the names differ in exactly the part that matters least.

Java allows a better arrangement. Several methods in the same class may share a single name, provided each can be told apart from the others by what it is given. Writing two or more methods with the same name and different parameter lists in one class is called method overloading.

NoteDefinition

Method overloading is the writing of two or more methods with the same name in the same class, distinguished from one another by the number, types or order of their parameters.

What makes two overloaded methods different

Section 9.2.1 defined the method signature as a method’s name together with the number, types and order of its parameters. That definition now does its real work: two methods in a class may share a name provided their signatures differ.

There are exactly three ways a signature can differ.

The number of parameters may differ — one measurement for a square, two for a rectangle.

The types may differ — area(int, int) and area(double, double) are distinct signatures even though both take two values.

The order of the types may differ. void issue(int copies, String title) and void issue(String title, int copies) are two different methods, because the types appear in a different order. This is legal, though a class that uses it is usually asking its callers to remember something they will get wrong.

What cannot distinguish two methods is anything outside the signature. The names given to the parameters are not part of it, so area(int side) and area(int length) are the same method written twice. Neither is the return type part of it, and this catches people out often enough to be worth a demonstration.

two methods differing only in return type What students write:

public static int measure(double metres)
{
    return (int) metres;
}

public static double measure(double metres)
{
    return metres * 100;
}

What happens: The class does not compile. Both methods have the signature measure(double), so as far as Java is concerned the second is a redefinition of the first, and the compiler reports that the method is already defined in the class. The fix: Give the two methods different parameter lists, or different names. It is worth seeing why the rule must be this way: at a call such as measure(2.5); written on a line of its own, nothing in the call says which return type is wanted, so there would be no way to decide which method to run.

Table 9.17 – pairs of headers, and whether they overload one another legally

First header Second header Legal? Reason
area(int side) area(int length, int breadth) Yes the number of parameters differs
area(int a, int b) area(double a, double b) Yes the types differ
issue(int n, String t) issue(String t, int n) Yes the order of the types differs
area(int side) area(int length) No same signature; parameter names are not part of it
measure(double m) measure(double m) returning a different type No same signature; the return type is not part of it
total(int m[]) total(int m[], int n) Yes the number of parameters differs
How Java decides which one to run

At every call, the compiler looks at the actual arguments — how many there are and what types they have — and picks the method whose parameter list matches. An exact match is preferred. If there is no exact match, Java will widen a value to reach one, so a call area(7.0, 4) can still reach area(double, double) by widening the int to a double.

The decision is made when the program is compiled, not while it runs, because everything needed to make it — the types written at the call — is already visible in the source. Fixing a call to a particular method at compile time in this way is called early binding, or static binding.

Diagram

Fig. 9.8-1 – left column headed “calls in main”, listing three lines one below the other: “area(5)”, “area(4, 3)”, “area(7.0, 4.0)”. Right column headed “methods in the class”, listing three headers one below the other: “int area(int side)”, “int area(int length, int breadth)”, “double area(double base, double height)”. Three straight arrows join each call to the matching header — first to first, second to second, third to third — and each arrow carries the label that justifies it: “one int”, “two ints”, “two doubles”. A caption reads “the actual arguments decide; the choice is made at compile time”

One name, three shapes

areas of a square, a rectangle and a triangle by one overloaded method name Problem: Compute the area of a square plot of side 5 metres, a rectangular plot 4 metres by 3 metres, and a triangular plot of base 7.0 metres and height 4.0 metres. All three must be computed by methods sharing the single name area.

class PlotArea
{
    public static int area(int side)
    {
        return side * side;
    }

    public static int area(int length, int breadth)
    {
        return length * breadth;
    }

    public static double area(double base, double height)
    {
        return 0.5 * base * height;
    }

    public static void main(String args[])
    {
        System.out.println("Square    = " + area(5));
        System.out.println("Rectangle = " + area(4, 3));
        System.out.println("Triangle  = " + area(7.0, 4.0));
    }
}

Sample output:

Square    = 25
Rectangle = 12
Triangle  = 14.0

Variable description:

[Table:] Table 9.18 – variables of class PlotArea, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | side | int | side of the square, received by the first method | | length | int | length of the rectangle, received by the second method | | breadth | int | breadth of the rectangle, received by the second method | | base | double | base of the triangle, received by the third method | | height | double | height of the triangle, received by the third method |

Notes: The three methods do not have to share a return type, and here they do not: the two whole-number calculations return int and the triangle returns double, which is why its answer prints as 14.0 rather than 14. Differing return types are allowed precisely because the return type plays no part in choosing between them. area(5) is matched by the count of arguments alone; area(4, 3) and area(7.0, 4.0) have the same count, and are separated by their types.

Q5. Can two methods in the same class be given exactly the same name, and if so, how does Java tell them apart?

A. Yes, and it is done deliberately whenever one operation is carried out on different kinds of data. Java tells them apart by their signatures — the number, types and order of the parameters. At each call it reads the actual arguments and selects the method whose parameter list they fit, an exact match being preferred and a widened match accepted if there is no exact one. What it never looks at is the return type or the parameter names, so two methods differing only in those are not two methods at all, and the class will not compile.

Q. Have you already used an overloaded method without realising it?

A. Constantly. System.out.println accepts an int, a double, a char, a String and more besides — those are different methods sharing one name, chosen by the type of what you pass. It is a fair demonstration of the point of overloading: you have never had to remember a separate name for printing each type.

When overloading helps and when it hurts

Overloading earns its place when the several methods really do perform the same operation. area is a good example: whichever one runs, the caller gets an area, and the reader of the call loses nothing by not knowing which.

It becomes a trap when the methods share a name but not a meaning. If process(int) files a record while process(String) prints a report, a reader of the call learns nothing from the name and must check the types to find out what the line does. The test to apply before overloading is simple: could you describe all the versions honestly in one short phrase? If you cannot, they need different names.

Add a fourth method area(int base, double height) to PlotArea and predict what area(7, 4.0) will now call. Then remove the third method and predict what area(7.0, 4.0) will do. Compile after each change and account for what you find.

9.9 Methods That Call Other Methods

Nothing said so far restricts calls to main. A method body may contain calls, exactly as main may, and the method it calls may itself call a third. A method invoked from inside another method, rather than from main, is called a nested method.

The word nested describes the calling, not the writing. Every method of a class is written separately, one after another, inside the class body. Java does not allow a method to be written inside another method, and the arrangement in this section is simply one method’s body containing a call to another.

writing one method inside another What students write:

    public static void main(String args[])
    {
        public static boolean isPrime(int n)
        {
            return true;
        }
    }

What happens: The class does not compile. Java expects statements inside a method body, and a method header is not a statement; the compiler stops at the inner header, reporting an illegal start of expression. The fix: Close main first, then write isPrime as a separate member of the class. Nesting happens through calls at run time, never through where the methods are typed.

Solving a problem in layers

The real subject of this section is not a rule of syntax but a way of working. When a task is too large to see whole, split it into smaller tasks, give each a name, and let each method do one of them and call the others.

Take this problem: display all the prime numbers between two given limits and report how many there are. Solving it in one block means holding three separate ideas in your head at once — deciding primality, running through a range, and keeping a count. Split into layers, it becomes:

Deciding whether one number is prime is a question about a single number. Give it to a method that takes a number and answers yes or no. That method needs to know nothing about ranges or counting.

Running through the range is a separate job. Give it a method that takes the two limits, tries every number in turn, asks the first method about each, displays those that qualify and returns the count.

And main states the task: obtain the count for a particular range and report it.

Each layer can then be checked on its own. If the count comes out wrong, the fault is either in deciding primality or in the loop, and testing the first method with a handful of numbers settles which. That narrowing is only possible because the layers are separate.

Diagram

Fig. 9.9-1 – three boxes stacked vertically, labelled from top to bottom “main”, “countPrimes”, “isPrime”. A downward arrow on the left runs from main to countPrimes labelled “countPrimes(10, 30)”, and another from countPrimes to isPrime labelled “isPrime(n), once for every number in the range”. On the right, an upward arrow runs from isPrime to countPrimes labelled “true or false”, and another from countPrimes to main labelled “the count, 6”. A caption reads “control travels down the calls and back up the returns, one layer at a time”

displaying the primes in a range using two methods Problem: Display all prime numbers between 10 and 30, separated by commas, and then display how many were found. Deciding whether a number is prime and running through the range must be done by two separate methods.

class PrimeRange
{
    public static boolean isPrime(int n)
    {
        int i;
        if (n < 2)
            return false;
        for (i = 2; i <= n / 2; i++)
        {
            if (n % i == 0)
                return false;
        }
        return true;
    }

    public static int countPrimes(int low, int high)
    {
        int n, count = 0;
        for (n = low; n <= high; n++)
        {
            if (isPrime(n))
            {
                if (count > 0)
                    System.out.print(", ");
                System.out.print(n);
                count = count + 1;
            }
        }
        System.out.println();
        return count;
    }

    public static void main(String args[])
    {
        int total = countPrimes(10, 30);
        System.out.println("Number of primes = " + total);
    }
}

Sample output:

11, 13, 17, 19, 23, 29
Number of primes = 6

Variable description:

[Table:] Table 9.19 – variables of class PrimeRange, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | n | int | in isPrime, the number being tested; in countPrimes, the number currently reached in the range | | i | int | trial divisor inside isPrime | | low | int | lower limit of the range | | high | int | upper limit of the range | | count | int | primes found so far, returned to main | | total | int | count received by main |

Notes: isPrime returns boolean, so the call reads as a condition — if (isPrime(n)) needs no comparison written after it. Trial divisors stop at n / 2 because no number above half of n can divide it exactly. The comma is printed before each prime except the first, which is why the test uses count > 0: it puts the separators between the numbers and leaves no stray comma at either end. isPrime is a pure method in the sense of Section 9.5, while countPrimes displays as well as returning, which is a compromise the problem itself asks for.

countPrimes over the shortened range 10 to 13

n isPrime(n) returns count before Printed at this step count after
10 false 0 nothing 0
11 true 0 11 1
12 false 1 nothing 1
13 true 1 , 13 2

Result: the display reads 11, 13 and the method returns 2. The trace shows the effect of the count > 0 test: the first prime is printed bare, and every later one is preceded by its separator.

Two things a nested call is not

A method calling another method is not the same as a method calling itself. When a method calls itself, the technique is recursion, and it brings a difficulty of its own — the calls must be made to stop somewhere — which is why it is treated separately. Everything in this section involves one method calling a different method, and the chain of calls is finite by inspection.

Nor does a nested call give the inner method any special access. isPrime cannot see count, and countPrimes cannot see i. Each method holds only its own arguments and local variables, exactly as Section 9.3.2 described, and the only traffic between them is the values handed in and the value returned.

Q. countPrimes calls isPrime, which is written above it. Would it still work if isPrime were written last in the class?

A. Yes. The order in which methods are written inside a class does not matter — the compiler reads the whole class before deciding anything, so a method may call another written anywhere in the same class. Order the methods for a human reader instead, which usually means putting the ones that state the task above the ones that carry out the details.

Add a method sumPrimes(int low, int high) to PrimeRange that uses the existing isPrime and returns the total of all primes in the range. Call it from main for the range 10 to 30 and predict the figure before running the program.

9.10 Algorithmic Problem Solving Using Methods

Everything in this chapter so far has been machinery. This section puts it to work, because the point of learning about methods is not to be able to describe a header — it is to be able to take a problem you have not seen before and break it into parts that can each be written, tested and trusted.

Before any Java is written, four questions settle the shape of every method.

What is the input? Name the values the method cannot work without. Those become its parameters, and nothing else should be smuggled in.

What is the output? Decide the single value the caller needs back. If the caller needs nothing back, the method is void and must do its work by displaying or by altering an object it was given.

What is the method of solution? State it in ordinary sentences or in numbered steps, before writing any code. If you cannot state it, you are not ready to write it.

And why is the method correct? Name the reason it must produce the right answer — the property being tested, the quantity being accumulated, the case being separated — rather than trusting that it worked on the one example you tried.

9.10.1 Number Problems

A great many number problems have the same shape: examine the digits or the divisors of a number, accumulate something as you go, and judge the number by what you accumulated. Once that shape is recognised, the work splits naturally into two methods — one that accumulates, one that judges.

Take perfect numbers. A number is perfect when its proper divisors — the divisors below the number itself — add up to exactly the number. The divisors of 28 are 1, 2, 4, 7 and 14, and those five numbers total 28, so 28 is perfect.

sum of the proper divisors of a number Input: a positive whole number n. Output: the sum of all divisors of n that are less than n. Method:

1. Set sum to 0.
2. For every i from 1 to n/2, repeat step 3.
3. If i divides n exactly, add i to sum.
4. Return sum.

Why it works: every proper divisor of n is at most n / 2, because a divisor larger than half of n would need a partner smaller than 2 to reach n. So the loop examines every candidate that could possibly qualify, and adds exactly those that do. Cost: the loop runs n / 2 times whatever the number turns out to be, so the cost is \(O(n)\), where \(n\) is the number being examined.

Judging the number is then a single comparison, and it belongs in a method of its own so that the test reads as what it is.

testing whether a number is perfect Problem: For the numbers 28 and 45, state whether each is a perfect number. The sum of the proper divisors and the test itself must be written as separate methods.

class PerfectNumber
{
    public static int sumOfFactors(int n)
    {
        int i, sum = 0;
        for (i = 1; i <= n / 2; i++)
        {
            if (n % i == 0)
                sum = sum + i;
        }
        return sum;
    }

    public static boolean isPerfect(int n)
    {
        return sumOfFactors(n) == n;
    }

    public static void main(String args[])
    {
        int a = 28, b = 45;
        if (isPerfect(a))
            System.out.println(a + " is a perfect number");
        else
            System.out.println(a + " is not a perfect number");
        if (isPerfect(b))
            System.out.println(b + " is a perfect number");
        else
            System.out.println(b + " is not a perfect number");
    }
}

Sample output:

28 is a perfect number
45 is not a perfect number

Variable description:

[Table:] Table 9.20 – variables of class PerfectNumber, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | n | int | number being examined, received by both methods | | i | int | candidate divisor tested inside sumOfFactors | | sum | int | running total of the divisors found so far | | a, b | int | the two numbers tested from main |

Notes: isPerfect contains no arithmetic of its own; it asks sumOfFactors for a figure and compares it. Written this way, the body of isPerfect states the definition of a perfect number almost word for word, which is the clearest evidence that the split was the right one. Both methods are pure: neither alters anything the caller holds. The method assumes a positive number — sumOfFactors(0) would return 0 and isPerfect(0) would answer true, which is a limitation worth guarding against if the number is ever supplied by a user rather than fixed in the program.

sumOfFactors for n = 12

i Does i divide 12? sum after this step
1 yes 1
2 yes 3
3 yes 6
4 yes 10
5 no 10
6 yes 16

Result: the method returns 16, and since 16 is not 12, isPerfect(12) answers false. The trace also shows why the loop must stop at n / 2: no number between 7 and 11 divides 12, and 12 itself is excluded by definition.

9.10.2 Finding Roots of an Algebraic Equation

The standard form of a quadratic equation is \(ax^2 + bx + c = 0\), with \(a \neq 0\). Its roots are given by

\[x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}\]

The quantity under the square root, \(b^2 - 4ac\), is called the discriminant, and it decides what kind of answer exists before any root is computed. If it is positive there are two different real roots; if it is zero the two roots coincide; if it is negative there are no real roots at all, and the roots are said to be imaginary.

That last case is the reason this problem is worth writing carefully. A program that computes the square root first and asks questions afterwards is taking the square root of a negative number, which is not a real value at all.

roots of a quadratic equation Input: the coefficients a, b and c of \(ax^2 + bx + c = 0\), with a not zero. Output: a statement of the roots, or of the fact that they are imaginary. Method:

1. Compute d = b*b - 4*a*c.
2. If d is greater than 0, compute (-b + square root of d) / (2*a)
   and (-b - square root of d) / (2*a), and report two distinct real roots.
3. Otherwise, if d equals 0, compute -b / (2*a) and report two equal roots.
4. Otherwise, report that the roots are imaginary.

Why it works: the three branches are decided by the sign of the discriminant, and the sign of the discriminant is exactly what determines whether the square root in the formula is of a positive quantity, of zero, or of a negative quantity. The square root is therefore never taken until it is known to be a real one. Cost: a fixed number of arithmetic operations whatever the coefficients, so the cost is \(O(1)\).

reporting the roots of a quadratic equation Problem: For the equations \(x^2 - 5x + 6 = 0\), \(x^2 - 4x + 4 = 0\) and \(x^2 + 2x + 5 = 0\), report the nature of the roots and their values where they are real. The discriminant must be computed by a method of its own.

class Quadratic
{
    public static double discriminant(double a, double b, double c)
    {
        return b * b - 4 * a * c;
    }

    public static void showRoots(double a, double b, double c)
    {
        double d, r1, r2, r;
        d = discriminant(a, b, c);
        if (d > 0)
        {
            r1 = (-b + Math.sqrt(d)) / (2 * a);
            r2 = (-b - Math.sqrt(d)) / (2 * a);
            System.out.println("Two distinct real roots: " + r1 + " and " + r2);
        }
        else if (d == 0)
        {
            r = -b / (2 * a);
            System.out.println("Two equal real roots: " + r);
        }
        else
            System.out.println("Roots are imaginary");
    }

    public static void main(String args[])
    {
        showRoots(1, -5, 6);
        showRoots(1, -4, 4);
        showRoots(1, 2, 5);
    }
}

Sample output:

Two distinct real roots: 3.0 and 2.0
Two equal real roots: 2.0
Roots are imaginary

Variable description:

[Table:] Table 9.21 – variables of class Quadratic, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | a, b, c | double | coefficients of the equation, received by both methods | | d | double | value of the discriminant for the current equation | | r1, r2 | double | the two distinct real roots, when they exist | | r | double | the repeated root, when the discriminant is zero |

Notes: showRoots returns nothing, and that is a decision rather than an oversight. The three cases produce three different kinds of answer — two numbers, one number, or none — and Section 9.2.3 established that a method returns exactly one value. Reporting is therefore the honest job for this method. The coefficients are declared double although whole numbers are passed at each call; the values are widened automatically, as Section 9.3.1 described. The method assumes a is not zero: with a equal to 0 the equation is not quadratic at all, and the division by 2 * a would not be meaningful.

taking the square root before testing the discriminant What students write:

        d = Math.sqrt(b * b - 4 * a * c);
        if (d > 0 || d == 0)

What happens: For \(x^2 + 2x + 5 = 0\) the quantity inside the root is −16, and Math.sqrt of a negative number returns the special value NaN, meaning “not a number”. Every comparison involving NaN is false, so both tests fail and the program falls through to the imaginary branch. It appears to work — which is the danger, because the same variable d is now sometimes a discriminant and sometimes a square root, and the equal-roots case can no longer be told apart reliably. The fix: Keep the discriminant and its square root as two separate steps. Test the discriminant first, and take the square root only inside the branch where it is known to be real.

9.10.3 Methods That Work on Arrays and Strings

When the data is a whole collection rather than a single number, nothing about method design changes — but two points from earlier sections start to matter together.

The collection is passed as a reference, so the method works on the caller’s own array or object rather than on a copy. And a method still returns at most one value, so a method that must report several findings either returns a collection or, as here, is called several times for different findings.

The problem below is about a line of text. Both methods read the sentence and report on it; neither alters it, which is just as well, since a String cannot be altered in any case.

initials of the words of a sentence Input: a sentence s, with words separated by spaces. Output: a string made of the first letter of every word, in order. Method:

1. Set result to the empty string.
2. Break the sentence into words.
3. For each word in turn, repeat step 4.
4. Join the first character of the word to the end of result.
5. Return result.

Why it works: the words are produced in the order they appear, and each contributes exactly one character to the end of the result, so the letters come out in the same order as the words they came from. Cost: every character of the sentence is examined once while the words are separated, so the cost is \(O(n)\), where \(n\) is the number of characters in the sentence.

counting the words of a sentence and forming its initials Problem: For the sentence Meera reads two books every week, display the number of words and a string made of the first letter of each word.

import java.util.StringTokenizer;

class SentenceInfo
{
    public static int countWords(String s)
    {
        StringTokenizer st = new StringTokenizer(s);
        return st.countTokens();
    }

    public static String initials(String s)
    {
        StringTokenizer st = new StringTokenizer(s);
        String word, result = "";
        while (st.hasMoreTokens())
        {
            word = st.nextToken();
            result = result + word.charAt(0);
        }
        return result;
    }

    public static void main(String args[])
    {
        String line = "Meera reads two books every week";
        System.out.println("Words = " + countWords(line));
        System.out.println("Initials = " + initials(line));
    }
}

Sample output:

Words = 6
Initials = Mrtbew

Variable description:

[Table:] Table 9.22 – variables of class SentenceInfo, with the type and purpose of each [/Table] | Variable | Type | Purpose | |—|—|—| | s | String | sentence received by both methods | | st | StringTokenizer | breaks the sentence into words, one method’s own copy | | word | String | word most recently extracted | | result | String | initials collected so far | | line | String | sentence held in main |

Notes: Each method creates its own StringTokenizer, and it must. A tokenizer is used up as it is read, so initials could not reuse the one built inside countWords even if it could reach it. Building result by repeated joining creates a new String at every step, because a String cannot be altered once made, as Section 9.4.3 explained; for a sentence of a few words that cost is trivial, and where text must be built up heavily the StringBuffer class exists for the purpose. Both methods are pure, and both would give the same answers however often they were called.

Q. countWords and initials both break the same sentence into words. Should that work be shared?

A. It would be worth sharing if the two methods were often called together on the same sentence, and the usual way is for one method to produce the words once and hand them on. Here each method is complete in itself and can be called alone, which is the more valuable property while the sentences are short. The judgement is the same one made in Section 9.1: share the work when the sharing names something real, not merely to avoid writing a line twice.

Practical link: This assignment exercises method design, formal and actual arguments, and calling one method from another, and should be filed in the work file under Methods (Functions). Write a class BusFareChart containing a method fare(int km) that returns the fare for a journey — 8 rupees for the first 3 kilometres and 4 rupees for every kilometre or part of a kilometre beyond that — and a second method chart(int from, int to) that uses fare to display the distance and the fare for every whole number of kilometres from from to to. Call chart(1, 6) from main, and submit the algorithm, the class and a variable description table. Test your program on the unseen input chart(3, 3) and make sure a single-row chart still prints correctly.

9.11 Summary

Why methods exist
  1. A method is a named block of statements, written once inside a class, that performs one specific task and can be executed from any point in the program by naming it. The block that names it is the caller.
  2. Repeating a block of statements instead of naming it costs more than space. The rule then lives in two places, so a correction made in one copy and missed in the other leaves a program that still compiles, still runs, and is quietly wrong.
  3. Abstraction is the practice of using an operation through its name and its stated effect, without needing to know how it is carried out. Naming a task lets a reader understand a program before deciding whether to study its details.
  4. A method earns its place by naming an operation meaningful on its own. Splitting a program into too many tiny methods can make it harder to follow, not easier, and every call costs a little time and memory.
Writing a method
  1. A method header states, in order, the access specifier, any modifier, the return type, the method name and the parameter list. Only the return type, the name and the brackets are compulsory.
  2. The access specifier decides where the method may be called from. Writing no specifier is not the same as writing public: the method is then reachable only from classes in the same package.
  3. The header quoted on its own is the method prototype. The method signature is narrower — the method name together with the number, types and order of its parameters. Parameter names and the return type are not part of the signature.
  4. The method body holds the statements. Variables declared inside it are local: they are created when the method is called, destroyed when it ends, and invisible from everywhere else.
  5. The return statement ends the method immediately and sends back exactly one value, of a type compatible with the declared return type. A method may contain several return statements, but only one of them runs on any given call.
  6. A method that returns nothing is declared void. It must do its work by displaying a result or by altering an object it was given.
  7. Two independent choices — whether values are received, and whether a value is returned — give the four forms of method header. Prefer to return a value; print only where display is genuinely the method’s purpose, because a method that prints its own answer has chosen the format for every future caller.
Calling a method
  1. A method runs only when called. At the call, control leaves the caller, the body executes, and control returns to the point immediately after the call, bringing the returned value with it.
  2. A call to a value-returning method becomes its value, so it may be written anywhere a value of that type is allowed. A call to a void method has no value and must stand as a complete statement.
  3. The variables declared in the header are the formal arguments; the values written at the call are the actual arguments. They are matched by number, by order and by type — never by name.
  4. An actual argument of a narrower type is widened automatically to reach a wider formal argument, which is automatic type coercion. The reverse is refused, because information would be lost.
  5. A formal argument lives for exactly one call. Each call works on its own private set, which is what makes a method safe to call repeatedly and from many places.
What crosses into a method
  1. What is copied at a call is always the value of the actual argument. For a primitive, that value is the data itself; for an array or an object, it is a reference — a value identifying where the object is stored.
  2. Passing a primitive gives the method a private copy, traditionally called pass by value, and nothing the method does to it can affect the caller.
  3. Passing an array or object copies the reference, so two references then lead to one object. Changes the method makes to the contents survive the call; this is traditionally called pass by reference.
  4. The traditional name is misleading in one respect. A method can change what the copied reference leads to, but reassigning the formal argument itself changes only where that local variable points, and the caller never sees it.
  5. A String and a wrapper such as Integer are immutable — no operation alters them, and every operation that appears to produces a new object instead. Passing one therefore behaves like passing a primitive.
Pure and impure methods
  1. The state of an object is the data it holds at a given moment. A pure method, or accessor, returns a value and changes the state of nothing. An impure method, or mutator, alters the state of an object during its call, and the change survives the call.
  2. A pure method called twice with the same data must give the same answer. Once a method is impure, the order in which calls are made begins to matter, and a change left behind by one call is called a side effect.
  3. A method that receives only primitives cannot be impure with respect to its caller, because it has no route back to the caller’s data. Impurity requires a reference.
Static and non-static members
  1. An instance variable is a class member of which every object holds its own copy. A static variable, also called a class variable, is a single copy belonging to the class and shared by every object, existing even when no object has been created.
  2. A static variable keeps its value between calls, which a local variable cannot do. That is the difference to reach for whenever a method must remember something from one call to the next.
  3. A static method belongs to the class and can be called without creating an object, which is why main must be static — it runs before any object exists. It may directly use only the static members of its class, since there may be no object for an instance member to belong to.
  4. A non-static, or instance, method belongs to each object and may use that object’s instance variables. It must be called on a named object, because the object named at the call is what decides whose data the method works on.
  5. Inside a non-static method, this is a reference to the current object — the one on which the method was called. It is required when a formal argument shadows an instance variable by sharing its name, and it cannot be used inside a static method.
Overloading
  1. Method overloading is the writing of two or more methods with the same name in the same class, distinguished by the number, types or order of their parameters.
  2. Two methods may share a name only if their signatures differ. Differing parameter names will not do, and neither will differing return types, because nothing at the call would say which return type was wanted.
  3. The compiler chooses between overloaded methods from the actual arguments written at the call, preferring an exact match and widening a value where there is none. The choice is fixed while the program is compiled, which is early binding, also called static binding.
Methods calling methods
  1. A method invoked from inside another method is a nested method. The nesting is in the calling, not in the writing — Java does not allow a method to be defined inside another method.
  2. A nested call grants no special access. Each method holds only its own arguments and local variables, and the only traffic between them is the values passed in and the value returned.
  3. Splitting a problem into layers, each a method, allows each layer to be tested on its own, which is what makes a wrong answer possible to locate rather than merely possible to notice.
  4. A method that calls itself is a different technique, recursion, with a difficulty of its own; every chain of calls in this chapter is finite by inspection.
Solving problems with methods
  1. Four questions settle the shape of a method before any code is written: what the input is, what the output must be, what the method of solution is, and why that method is correct.
  2. Many number problems share one shape — examine the digits or divisors, accumulate as you go, and judge the number by what was accumulated — which splits naturally into a method that accumulates and a method that judges. Summing the proper divisors of a number costs \(O(n)\), where \(n\) is the number being examined.
  3. For a quadratic equation in the standard form \(ax^2 + bx + c = 0\) with \(a \neq 0\), the discriminant \(b^2 - 4ac\) decides the nature of the roots before any root is computed: positive gives two distinct real roots, zero gives two equal roots, and negative means the roots are imaginary. The square root must not be taken until the discriminant is known to be non-negative. Reporting the roots costs \(O(1)\), since a fixed number of operations is performed whatever the coefficients.
  4. A method reporting several kinds of answer cannot return them all, since a return statement carries one value; such a method reports its findings instead of returning them.
  5. Forming the initials of the words of a sentence costs \(O(n)\), where \(n\) is the number of characters in the sentence, since each character is examined once as the words are separated.

9.12 Key Terms

  1. Abstraction — using an operation through its name and its stated effect, without needing to know how the operation is carried out.
  2. Accessor — another name for a pure method, because it accesses data without altering it.
  3. Access specifier — the part of a method header that decides which other classes may call the method; public, private, protected, or none at all.
  4. Actual arguments — the values written inside the brackets at the point where a method is called.
  5. Automatic type coercion — the widening of a value of a narrower type to a wider one, performed by Java without being asked, as when an int is handed to a double formal argument.
  6. Caller — the block of code that contains the call to a method, and to which control returns when the method ends.
  7. Class variable — another name for a static variable, so called because it belongs to the class rather than to any object.
  8. Current object — the object on which a non-static method was called, and the object to which this refers for the length of that call.
  9. Discriminant — the quantity \(b^2 - 4ac\) of a quadratic equation \(ax^2 + bx + c = 0\), whose sign decides whether the roots are two distinct real values, two equal real values, or imaginary.
  10. Early binding — the fixing of a call to a particular method while the program is compiled, using the types written at the call; also called static binding, and the way an overloaded call is resolved.
  11. Formal arguments — the variables declared in a method header, which receive the values supplied at the call.
  12. Immutable — of an object, having contents that cannot be altered after it is created; any operation appearing to change it produces a new object instead, as with String and the wrapper classes.
  13. Impure method — a method that changes the state of an object during its call, the change remaining in effect after the method ends.
  14. Instance method — another name for a non-static method, one belonging to each object rather than to the class.
  15. Instance variable — a class member of which every object holds its own separate copy.
  16. Lifetime — the period for which a variable exists; that of a formal argument or local variable is a single call.
  17. Local variable — a variable declared inside a method body, created when the method is called and destroyed when it ends.
  18. Method — a named block of statements, written once inside a class, that performs one specific task and can be executed from any point in the program by naming it.
  19. Method body — the statements of a method, enclosed between the braces that follow its header.
  20. Method call — the statement or expression that names a method and sets it running.
  21. Method header — the first line of a method, stating its access specifier, any modifier, its return type, its name and its parameter list.
  22. Method overloading — the writing of two or more methods with the same name in one class, distinguished by the number, types or order of their parameters.
  23. Method prototype — the method header quoted on its own, describing how a method is used without giving its statements.
  24. Method signature — a method’s name together with the number, types and order of its parameters; the parameter names and the return type form no part of it.
  25. Mutator — another name for an impure method, because it mutates the state of an object.
  26. Nested method — a method invoked from inside another method rather than from main; the nesting lies in the calling, since Java does not allow one method to be written inside another.
  27. Non-parameterised method — a method whose header has empty brackets, receiving no values from its caller.
  28. Non-returnable method — a method declared void, which sends no value back to its caller.
  29. Non-static method — a method belonging to each object of a class, which may use that object’s instance variables and must be called on a named object.
  30. Parameterised method — a method whose header declares one or more formal arguments.
  31. Parameter list — the declarations inside the brackets of a method header, each giving the type and name of one formal argument.
  32. Pass by reference — the traditional name for what happens when an array or object is passed to a method: the reference is copied, so both names lead to one object and changes made to its contents survive the call.
  33. Pass by value — the traditional name for what happens when a primitive is passed to a method: the value is copied, and nothing the method does to its copy can affect the caller.
  34. Pure method — a method that returns a value and changes the state of no object, so that calling it twice with the same data gives the same answer both times.
  35. Reference — a value identifying where an object is stored, held in a variable of a class or array type; the variable does not contain the object itself.
  36. Return statement — the statement that ends a method immediately and sends back exactly one value of a type compatible with the declared return type.
  37. Return type — the type of the single value a method sends back, written immediately before the method name, or void where nothing is sent back.
  38. Returnable method — a method whose return type is something other than void, and which therefore hands a value back to its caller.
  39. Scope — the region of a program in which a name may be used; that of a formal argument or local variable is the method body.
  40. Shadowing — the hiding of one name by a nearer declaration of the same name, as when a formal argument shares the name of an instance variable.
  41. Side effect — a change left behind by a method other than the answer its caller asked for.
  42. State — the data held by an object at a given moment.
  43. Static method — a method belonging to the class itself, callable without creating an object, and able to use directly only the static members of its class.
  44. Static variable — a class member of which exactly one copy exists, belonging to the class and shared by every object made from it, whether or not any object has been created.

9.13 Points to Ponder

  1. A pure method can be trusted to leave your data as it found it, yet some jobs — moderating a list of marks, sorting an array, filling a tank — are impure by their very nature, and returning a fresh copy each time would be wasteful. Where would you draw the line between a side effect that is the point of the method and a side effect that is a fault in it, and could that line be stated as a rule a reader could apply, or does it remain a judgement made case by case?

  2. Java refuses two methods that differ only in their return type, because a call written on a line of its own would not say which was wanted. But a call such as int n = measure(2.5); does say. Would a language be better or worse for allowing the return type to take part in choosing the method, and what would then happen to a call whose result is simply thrown away?

  3. Overloading lets one name serve several methods, and System.out.println shows how much convenience that can buy. Names, however, are free — nothing stops you inventing a separate name for every version. What exactly does a reader of a call gain from the shared name, and at what point does sharing it start to hide a difference that the reader needed to see?

  4. A method that receives everything it needs through its arguments can be tested on its own, with values of your choosing. A method that reads a static variable cannot, because its answer depends on something that happened earlier and elsewhere. Given that, when is a static variable genuinely the right choice rather than a convenience that has quietly made the method harder to trust?

  5. The keyword this may be left out almost everywhere, and most programmers do leave it out. Suppose a version of Java required it on every mention of an instance variable inside a non-static method. Which mistakes shown in this chapter would that rule have made impossible, what would it cost in readability, and would you accept the trade?

  6. Handing an array to a method gives that method the power to alter data the caller still owns, and nothing in the header warns the caller that it might. Should a language let a caller pass a collection in a form the method is forbidden to modify — and if such a facility existed, would you expect programmers to use it by default, or only where they had already been bitten?

9.14 Objective Questions

  1. Which of the following forms no part of a method signature? [1 mark]
  1. the method name
  2. the number of parameters
  3. the return type
  4. the order of the parameter types
  1. Which kind of method can be called without creating any object of its class? [1 mark]
  1. an instance method
  2. a static method
  3. an impure method
  4. a non-returnable method
  1. A method whose return type is void: [1 mark]
  1. returns the value 0
  2. returns no value at all
  3. must return a value of any one primitive type
  4. may be assigned to a variable of type int
  1. Which of these pairs of headers cannot exist together in one class? [1 mark]
  1. void show(int a) and void show(double a)
  2. int show(int a) and double show(int a)
  3. void show(int a) and void show(int a, int b)
  4. void show(int a, String b) and void show(String a, int b)
  1. Assertion (A): A method that receives only primitive arguments cannot alter the variables of its caller. Reason (R): A primitive actual argument is copied into the formal argument at the time of the call. Choose the correct option: [1 mark]
  1. Both (A) and (R) are true and (R) is the correct explanation of (A).
  2. Both (A) and (R) are true but (R) is not the correct explanation of (A).
    1. is true but (R) is false.
    1. is false but (R) is true.
  1. Assertion (A): The keyword this cannot be used inside a static method. Reason (R): A static method may be executed at a time when no object of its class exists. Choose the correct option: [1 mark]
  1. Both (A) and (R) are true and (R) is the correct explanation of (A).
  2. Both (A) and (R) are true but (R) is not the correct explanation of (A).
    1. is true but (R) is false.
    1. is false but (R) is true.
  1. Match each term in Column A with its description in Column B. [1 mark]
Column A Column B
a. pure method i. written inside the brackets at the point of call
b. mutator ii. leaves the state of every object unchanged
c. formal argument iii. declared in the method header
d. actual argument iv. alters the state of an object during its call
  1. Fill in the blank: the keyword ______ makes a member belong to the class itself rather than to any object made from it. [1 mark]

  2. Fill in the blank: when a formal argument is given the same name as an instance variable, the instance variable is hidden inside that method, and this hiding is called ______. [1 mark]

  3. State whether the following is true or false: a method may contain several return statements, but only one of them can execute during any single call. [1 mark]

9.15 Check Your Concepts

  1. Define a pure method, and name one task for which a pure method would be the appropriate choice. [2 marks]

  2. State the difference between a method prototype and a method signature. [2 marks]

  3. What is meant by the lifetime of a formal argument? Using your answer, explain why two calls to the same method cannot interfere with each other. [2 marks]

  4. Differentiate between a static method and a non-static method, giving three points of difference. [3 marks]

  5. Differentiate between actual arguments and formal arguments, giving three points of difference. [3 marks]

  6. Java refuses to compile a class containing two methods whose headers differ only in their return type. Give reasons for this restriction, referring to what is written at the point of call. [3 marks]

  7. A method is given an int and changes it; another method is given an array and changes one of its elements. After both calls, the caller finds the array altered but the int unchanged. Explain this difference in terms of what is copied when a method is called. [4 marks]

  8. Explain, with a suitable example of your own, one situation in which the keyword this is necessary rather than optional, and state what the program would do instead if this were omitted in that situation. [4 marks]

  9. A method may report its result either by returning a value or by displaying it. Explain how you would choose between the two, illustrate your answer with two contrasting examples of your own, and state one disadvantage of each choice. [6 marks]

  10. A single method is to be replaced by two: one that decides whether a given number has a stated property, and one that runs through a range of numbers using the first. Explain the advantages of this split for testing and for locating a fault, and give the headers of both methods for the task of listing every number in a range whose digits add up to a multiple of 3. [6 marks]

9.16 Output & Debugging

  1. Predict the output of the following program and give a reason for the value of each of the two numbers printed. [3 marks]
class Counter
{
    static int n = 5;

    public static int change(int n)
    {
        n = n + 10;
        return n;
    }

    public static void main(String args[])
    {
        int r = change(n);
        System.out.println(n + " " + r);
    }
}
  1. The following method is part of a class. Assume that both arguments are positive whole numbers. [3 marks]
    public static int mystery(int a, int b)
    {
        int c = 0;
        while (a >= b)
        {
            a = a - b;
            c = c + 1;
        }
        return c;
    }
  1. What value is returned by mystery(17, 5)?
  2. State in one line what the method computes in general.
  3. State what would happen if b were 0, and why.
  1. Predict the exact output of the following program, and state for each of the three calls which method was selected and why. [3 marks]
class Choose
{
    public static void test(int x)
    {
        System.out.println("int version " + x);
    }

    public static void test(double x)
    {
        System.out.println("double version " + x);
    }

    public static void main(String args[])
    {
        char c = 'A';
        test(7);
        test(7.0);
        test(c);
    }
}
  1. The following method is intended to return a grade for any mark between 0 and 100. Identify the single error, state what the compiler will do with the method as written, and rewrite the method correctly. [2 marks]
    public static String grade(int marks)
    {
        if (marks >= 40)
            return "Pass";
    }
  1. Predict the output of the following program. Explain why the value printed is not 2. [3 marks]
class Tally
{
    static int count = 0;

    public static void tick()
    {
        int count = 0;
        count = count + 1;
    }

    public static void main(String args[])
    {
        tick();
        tick();
        System.out.println(count);
    }
}

9.17 Programming Practice

  1. An electricity board charges for domestic supply as follows: 3 rupees for each of the first 100 units, 5 rupees for each of the next 100 units, and 7 rupees for every unit beyond 200. Define a class Electricity containing a method charge(int units) that returns the amount payable, and a main method that displays the amount for 250 units. Submit the algorithm, the complete class, and a variable description table. For 250 units the amount payable is 1150 rupees; test your program also with 90 units. [6 marks]

  2. Define a class Register containing two methods that share the name find. The first, find(int roll[], int key), returns the position at which key occurs in the array, or −1 if it does not occur. The second, find(String name[], String key), does the same for an array of names. Define main to create both arrays, call each method once, and display the results. Submit the algorithm for one of the two methods, the complete class, and a statement of why the two methods are permitted to share a name. [6 marks]

  3. Write a single method int digitSum(int n) that returns the sum of the digits of a positive whole number, together with a variable description table for it. State whether the method is pure or impure, with a reason. The method alone is required, not a complete class. [4 marks]

  4. Define a class SavingsAccount in which the rate of interest is the same for every account but the balance is not. The class must contain: an instance variable for the balance; a static variable for the rate; a static method setRate(int rate) that fixes the rate for all accounts; a method deposit(int balance) that adds the amount to the account’s own balance, using this to separate the argument from the instance variable; and a method interest() that returns the interest earned on the balance at the current rate. Define main to set the rate once, create two accounts, deposit different amounts into each, and display the interest earned by each. Submit the complete class and a variable description table, and state which of your members had to be static and why. [6 marks]