Overloading Subroutines
The signature of a subroutine does not include its return type.
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
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.
printLine()is written once, as a named blockmain()callsprintLine()three times at different points- Each call makes the program run the block again from the start
----
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.
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.
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.
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.
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?
Reveal 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
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?
- 1Compare 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.
- 2Identify 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.
- 3Change 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.
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.
- 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.
| Design | Would it be legal? | What distinguishes the routines |
|---|---|---|
| Several getln() routines that differ only in return type | No, a syntax error | Nothing in the signature |
| getlnInt() and getlnDouble() | Yes | Different 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.
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 likestatic int getln() and static double getln() in the same classWhy it failsThe signature does not include the return type, so both have the same signature.
Do this insteadGive 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 failsA subroutine runs only when the program reaches a point where it calls it.
Do this insteadTrace 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
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()andgetlnDouble()instead of onegetln()name. ThreeN2.javauses subroutines to organize its code and to help format its output in neat columns.