Concepts / Subroutines and Formal Parameters

Subroutines and Formal Parameters

The scope of a name is the part of the source code where that name is valid, and it is determined by where the declaration appears.

  • Programming

One Block of Code, Many Calls

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 do you think happens?

If a program needs the same calculation or the same printed output in several places, where would you rather keep the instructions: in one place, or in every place they are needed?

Reveal answer
Answer:

In one place. A subroutine lets the instructions be written once and then called from each place they are needed.

The point of a subroutine is that it is written once and called many times. Each call reuses the same block of code rather than repeating its lines.

The important word in that description 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 one run of the program. To understand how the compiler decides which name means what across all those moments, we need the idea of scope.

What Scope Means for a Name

When Java executes a variable declaration, memory is allocated for that variable. The name can then be used in some part of the source code to refer to that memory, or to the data stored there. The part of the source code where the name is valid is called its scope. The same idea applies to subroutine names and to formal parameter names.

Definition

The scope of a name is the part of the source code where that name is valid. It is determined by where the declaration appears, not by what the program does while it is running.

Key point

Scope is a property of the source text. Reading the code tells you the scope of a name; you do not have to run the program to find it out.

Two common assumptions cause confusing errors. The first is that once a name is declared, it can be used anywhere in the program. The second is that a new declaration with an old name simply overwrites the old one. Java does not let a name float freely across a program. Each name has a region of source code where it is meaningful, and understanding that region is the key to reading unfamiliar code correctly.

When a Local Name Hides a Member Variable

Java allows a local variable or formal parameter to share a name with a member variable. Inside the scope of that local variable or parameter, the member variable is hidden: the simple name refers to the local one. The member variable is not destroyed or made inaccessible. It is simply not reachable by its simple name in that region.

java
Output
0
5
0
Notes on the code
  • count on the static line is a member variable in scope across the whole Game class
  • Inside playGame(), the local declaration int count = 5 hides the member variable for the simple name count
  • Game.count uses the full name and reaches the hidden member variable even inside playGame()
  • In main(), there is no local count, so the simple name refers to the member variable
class Gamewhole class is the scopestatic countmember variable, value 0local countvalue 5, hides the memberhereplayGame bodybody is a nested regionGame.countfull name reaches thememberdeclaresdeclaresdeclaresfull name bypasses hiding
Which declaration does the simple name count refer to at each point, and how is the hidden member reached by its full name?

Read the example as a map. Inside the body of playGame(), the simple name count points to the local variable. Everywhere else in the Game class, the simple name count points to the member variable, unless some other local variable or parameter named count hides it in that region. The member variable can still be reached through its full name, Game.count, and this full name may be used even inside the class, including inside playGame(). The general rule is that the simple name of a hidden member variable must be replaced by its full form, className.variableName, to reach it.

Why a Redeclared Name Is an Error

Many languages would allow an inner declaration to hide an outer one. Java adds a stricter rule on top of the scope regions: it is not legal to redefine the name of a formal parameter or local variable within its scope, even in a nested block. The compiler rejects the second declaration, which is a compile-time error.

java
Notes on the code
  • total is declared at the top of main() and its scope runs to the end of main()
  • The second declaration of total is inside the nested for block, which is still within the scope of the first
  • Java rejects this with a compile-time error, so the program will not run

The same rule that forbids overlapping declarations also explains when a name becomes free again. Once the block in which a variable was declared ends, its scope has ended, and the name is available for a new declaration. Java does not keep the old name reserved after its region closes. This is why two separate for loops can each declare their own i.

java
Output
0
1
0
1
2
Notes on the code
  • The first i belongs only to the first for statement
  • When that statement ends, its name is free, so the second for statement may declare a new i
  • Neither loop sees the other's i

Subroutine Names and Variable Names

A natural question follows: can a local variable hide a subroutine name the same way it hides a member variable? The source says this cannot happen. Java has no rule forcing variables and subroutines to have different names, so the same name may legally be used for both in one class. The compiler can always tell which kind of name is meant, because a subroutine name is always followed by a left parenthesis.

This is also why subroutine names are often written with parentheses, as in main(). The source suggests thinking of the parentheses as part of a subroutine's name. Class names follow similar logic: a class name is a type, so it can be used to declare variables and formal parameters, and to specify a subroutine's return type. The source gives a deliberately extreme case, a class called Insanity whose subroutine has Insanity as its name, its return type, and the type of its parameter. Its verdict is that legal does not mean wise, so that example demonstrates the rules rather than a style to imitate.

Tracing Repeated Calls from main

Now trace what happens during a run. The main program is the place where execution starts, and each call to a subroutine transfers control to that subroutine's body. When the subroutine finishes, control returns to main at the point just after the call, and main continues. A single subroutine can therefore be entered at several points in the same run, once for each call.

java
Output
1
-1
2
-1
3
Notes on the code
  • printSeparator is written once, but it is called twice from main
  • Each call sends control into the subroutine, and the program resumes in main right after the call
  • The output shows the subroutine's line appearing at both call points
mainstarts the runprintSeparatorsame body each timeprints 1call 1prints -1, returnsprints 2call 2prints -1, returnsprints 3
In what order do main and the subroutine execute across the two calls, and where does control return to main after each call?

Read the diagram top to bottom. The first call runs printSeparator and then control returns to main, which prints 2. The second call repeats the same subroutine body and returns again, so main prints 3. Both calls use one copy of the code, and the position in main determines where execution resumes.

Formal Parameters at the Call Site

A subroutine can also declare formal parameters. A formal parameter's scope is the body of its subroutine, so its name exists only inside that body. The caller has its own names in its own scope. In the example below, the name x lives in main, while the name n lives only inside showSquare. The two names are different declarations in different regions of source code.

java
Output
16
49
Notes on the code
  • n is a formal parameter whose scope is the body of showSquare
  • x is a local variable of main, and it is not visible inside showSquare
  • The same subroutine is called twice with different arguments
x in mainvalue 4, scope is mainshowSquare(x)call site in mainn in showSquareformal parameter, scope isits body16printed by n * nargumentreceived asn * n
When main calls showSquare with x, which name lives where, and which scope does each one belong to?

Mistakes Beginners Make with Scope

  • Common mistake

    Assuming a declared name can be used anywhere in the program

    What it looks like

    Using a local variable total in one method and expecting it to exist in another method

    Why it fails

    A local variable's scope runs only to the end of the block where it was declared, so the name is not valid elsewhere.

    Do this instead

    Use the name only inside the region where it was declared, or pass the value through a formal parameter.

  • Common mistake

    Assuming a new declaration simply overwrites the old one

    What it looks like

    Declaring int total = i inside a nested block while an outer int total is still in scope

    Why it fails

    Java forbids redefining a formal parameter or local variable name within its scope, even in a nested block, so this is a compile-time error.

    Do this instead

    Choose a different name for the inner variable, or move the declaration outside the nested block if that is what you intend.

  • Common mistake

    Expecting the simple name to reach a hidden member variable

    What it looks like

    Writing count inside playGame() and expecting the static member value 0 when a local count is declared there

    Why it fails

    The local declaration hides the member variable for the simple name in that region.

    Do this instead

    Write the full name Game.count to reach the member variable.

  • Common mistake

    Believing a loop counter remains available after its for statement

    What it looks like

    Using i after a for loop that declared int i in its header

    Why it fails

    A loop control variable declared in the for header is valid only within that for statement.

    Do this instead

    Declare a new variable before the loop if its value must be used afterward.

How ThreeN2.java Uses Subroutines

The source describes ThreeN2.java, introduced in Section 4.4 as an improved version of the 3N+1 program, as a program that uses subroutines and prints its output in neat columns. It is the source's own concrete example of the idea in an actual program.

  • Code organization: separating work into named blocks makes the program easier to read and manage
  • Output formatting: the program prints its results in neat columns, and its subroutines are part of how that formatting is achieved

The source does not list the individual subroutines inside ThreeN2.java or show their code, so this article does not describe them in detail. When you open the file yourself, look for the places where the program calls a named block of code, and compare them with the places where it prints results. Those two sets of places show how the subroutines organize the program and shape its columns.

Practice: Find the Scope and the Output

MEDIUM

Given the class below, write the output and state which declaration the simple name count refers to inside playGame() and inside main(). Explain how to reach the other declaration.

public class Practice {
    static int count = 2;
    static void playGame() {
        count = 7;
        int count = 3;
        System.out.println(count);
    }
    public static void main(String[] args) {
        playGame();
        System.out.println(count);
    }
}
Hints
  • Ask which declaration of count is in scope on each line, starting from the declaration inside playGame()
  • Recall that a local declaration hides the member variable for the simple name within its region
  • Consider which line is the one that comes before int count = 3 in the same block, and what that means for its scope

Key Takeaways

  • The scope of a name is the part of the source code where that name is valid, and it is determined by where the declaration appears.
  • A static member variable or static subroutine is in scope across its entire class, and a local variable or formal parameter may hide a member variable, which must then be reached through its full name, className.variableName.
  • Java forbids redefining a formal parameter or local variable name within its scope, even in a nested block, but the name becomes reusable once its block ends.
  • A subroutine is a block of code that can be called multiple times from different parts of a program, and it runs each time a call reaches it.
  • ThreeN2.java uses subroutines to organize its code and to format its output in neat columns.