Concepts / Overloading Subroutines

Overloading Subroutines

The signature of a subroutine does not include its return type.

  • Programming

Writing Once, Using Many Times

Imagine a program that needs the same job done in three different places. You could copy the same lines of code three times, or you could write those lines once and let the rest of the program use them whenever they are needed. A subroutine is the second option. It is a block of code that can be called multiple times from different parts of a program.

What a Subroutine Does When Called

Definition

A subroutine is a block of code that can be called multiple times from different parts of a program. Writing it once lets the program reuse it at many points.

The important word is called. A subroutine does not run just because it exists in a program. It runs when the program reaches a point where it calls it. Because the same block can be called more than once, a single piece of code can serve many moments in a single run of the program.

java
Notes on the code
  • printLine() is written once, as a named block
  • main() calls printLine() three times at different points
  • Each call makes the program run the block again from the start
Output
----
A
----
B
----

Tracing Calls Across a Single Run

The diagram below follows the program above in time order. Control starts in main(), passes to printLine() at each call, and comes back to main() when the block finishes. The same subroutine body is entered three times, and each time execution resumes in main() at the line after the call.

main()printLine()call 1return 1, prints dashescall 2return 2, prints dashescall 3return 3, prints dashes
In what order do control and data pass between main() and the subroutine across successive calls, and where does execution return each time?

Anatomy of a Subroutine Signature

Overloading depends on a concept called the signature of a subroutine. The signature is the part of the declaration that the compiler uses to tell subroutines apart. The source states plainly that the signature of a subroutine does not include its return type. In the declaration below, the return type sits outside the signature, while the name and the parameter list are the parts that make it up.

doublereturn type, outside thesignatureaveragename, part of the signature(int a, int b)parameter list, part of thesignature
Which parts of a subroutine declaration make up its signature, and which part sits outside it?

Why Return Type Cannot Separate Two Subroutines

When a beginner first meets overloading, a natural guess is that the compiler should be able to tell two versions of a subroutine apart by looking at everything about them. The name, the parameters, and the type of value the subroutine hands back all seem like reasonable clues. The return type is the clue that does not work.

Definition

Overloading means giving several subroutines in the same class the same name, and relying on the differences in their signatures to tell them apart. Overloads must be distinguished by their signatures, so they need different parameters or different names.

Since the signature does not include the return type, the only things that can make two same-named subroutines distinct are the things the signature does contain. A pair that differs only in what it returns gives the class nothing to distinguish them by. That is why the arrangement is illegal: two subroutines in the same class with the same signature but different return types is a syntax error.

getln()returns intgetln()returns doublesame name and parameters, so a syntax error
Why does the compiler see these two declarations as the same signature, so that only one can exist in the class?

Predicting a Same-Name Conflict

What do you think happens?

Two subroutines in the same class are both named getln and take no parameters, but one returns int and the other returns double. Can both legally exist in that class?

  • Yes, because their return types are different

  • No, because this is a syntax error

Reveal answer
Answer:

No, because this is a syntax error

The signature does not include the return type. Both declarations have the same name and the same (empty) parameter list, so the class has nothing to distinguish them by, and the pair is illegal.

Fixing a Conflict with Parameters

Worked example

Making two square routines legal

A class needs two routines that square a value. The first version is written with int parameters and an int return type. The second version is written with the same name and parameter list but returns double. Is this legal, and how can it be fixed?

  1. 1
    Compare the signatures

    Both declarations are named square and take one int parameter. The return types differ, but the signature does not include the return type, so the signatures are identical.

  2. 2
    Identify the result

    Two subroutines in the same class with the same signature but different return types is a syntax error, so the pair cannot coexist.

  3. 3
    Change the parameters

    Overloads must be distinguished by their signatures, so one option is to give the second version a different parameter type, such as double, which makes its signature differ from the first.

Result

Keeping square(int) returning int and writing square(double) returning double is legal, because the parameter lists differ. Writing two versions that differ only in return type is not.

java
Notes on the code
  • The first square takes an int and returns int
  • The commented-out line would be a syntax error
  • The last square takes a double, so its signature is different

How TextIO Sidesteps the Problem

The TextIO class is a useful illustration of the rule in action. A programmer might expect its input routines for reading different types of values to share one name, such as getln, with the return type telling the caller what kind of value came back. The rule forbids that design, so TextIO does not use it. In a given class, there can only be one routine named getln that takes no parameters.

DesignWould it be legal?What distinguishes the routines
Several getln() routines that differ only in return typeNo, a syntax errorNothing in the signature
getlnInt() and getlnDouble()YesDifferent names

Two ways to provide input routines for different value types

Because the input routines are distinguished by having different names, such as getlnInt() and getlnDouble(), the caller chooses the right routine by name. The overloading rule is not broken, since each name is unique within the class.

Where ThreeN2.java Uses Subroutines

The source points to a concrete program that uses subroutines: ThreeN2.java, introduced in Section 4.4 as an improved version of the 3N+1 program. The source describes it as a program that uses subroutines and prints its output in neat columns.

The source gives two reasons that ThreeN2.java relies on subroutines. The first is code organization: separating work into named blocks makes the program easier to read and manage. The second is output formatting: the program prints its results in neat columns, and the subroutines are part of how that formatting is achieved. The source does not list the individual subroutines inside the file, so this article does not describe them in detail.

EASY

Open ThreeN2.java yourself. Look for the places where the program calls a named block of code, and compare them with the places where it prints results. Note which calls are made from main() and which blocks are responsible for printing lines in columns.

Hints
  • Start by finding main() and listing every named block it calls.
  • Then find the lines that print output and see whether a called block produces them.

Common Mistakes with Return Types

  • Common mistake

    Believing that a different return type makes two same-named subroutines legal

    What it looks like
    static int getln() and static double getln() in the same class
    Why it fails

    The signature does not include the return type, so both have the same signature.

    Do this instead

    Give the overloads different parameter lists, or give them different names as TextIO does with getlnInt() and getlnDouble().

  • Common mistake

    Thinking a subroutine runs just because it is written in the file

    Why it fails

    A subroutine runs only when the program reaches a point where it calls it.

    Do this instead

    Trace the calls from main() to see exactly when each block runs.

  • Common mistake

    Copying the same lines into several places instead of writing a subroutine once and calling it

Checking Your Understanding

EASY

A class contains a subroutine printHeader() that takes no parameters and prints a title. A program calls printHeader() at the start of main() and again after a loop finishes. Explain how many times the block runs, and why one written copy is enough.

Hints
  • Count the call sites in main(), not the lines in the subroutine body.
  • Think about what the program does each time it reaches a call.

Key Takeaways

  • A subroutine is a block of code written once that can be called multiple times from different parts of a program, and it runs only when the program calls it.
  • The signature of a subroutine does not include its return type, so two subroutines in the same class that differ only in return type is a syntax error.
  • Overloads must be distinguished by different parameters or different names, which is why TextIO uses getlnInt() and getlnDouble() instead of one getln() name.
  • ThreeN2.java uses subroutines to organize its code and to help format its output in neat columns.