Formal and Actual Parameters
Formal parameters are the variables declared in a subroutine's parameter list, and actual parameters are the values or expressions supplied in the call statement.
Predicting Where Parameter Values Come From
Subroutines are reusable blocks of code that perform a task, and they often need outside information to do their work. Parameters are the mechanism that carries that information in. Using a subroutine that already exists usually feels natural: you write the call, supply some values, and get a result back. The difficulty appears when you have to write the subroutine yourself, because then you must decide what the parameter variables are supposed to hold and when they receive their values.
What do you think happens?
A subroutine's parameter list declares three names, and another part of the program calls it with three expressions. Where do the three parameter variables get their values, and at which point in time?
Reveal answer
The caller's actual values are assigned to them before the subroutine body runs.
The computer assigns actual parameter values to formal parameters before it executes the statements inside the subroutine, so the body never needs to set or request them. Keep this prediction in mind, because the rest of the article shows why it is correct and why the other two options reflect a fundamental misunderstanding.
Formal Parameters Versus Actual Parameters
The two terms are easy to mix up because they appear side by side in the same line of code. A useful way to keep them apart is to ask where each one is written. A formal parameter is a variable declared in a function definition, and it is the name the subroutine uses internally. An actual parameter is the value or expression supplied where the subroutine is used, and it is the value the caller provides.
Formal parameters are the variables declared in a subroutine's parameter list. Actual parameters are the values or expressions supplied in the call statement. A formal parameter is written in the definition; an actual parameter is written at the place where the subroutine is used.
The formal parameter header only announces that a named slot exists and what kind of data it accepts. The header contains no values. Filling those slots is the job of the call. Keeping this split in mind prevents most of the confusion that follows.
Reading the doTask Call Piece by Piece
The source uses one call as its running example. Read it first, before worrying about vocabulary. A good first habit is to form a prediction about what each piece of the call does, then name the pieces.
- 17 is a plain value, a literal integer
Math.sqrt(z+1)is an expression whose result is a single number- z >= 10 is a comparison that produces a true or false result
Identifying each actual parameter
List the three actual parameters in the call doTask(17, Math.sqrt(z+1), z >= 10), giving each one's position and what kind of thing it is.
- 1Locate the parentheses
The actual parameters sit inside the parentheses of the call, separated by commas.
- 2Position 1
The first actual parameter is 17. It is a plain value, so nothing needs to be computed before it is passed.
- 3Position 2
The second actual parameter is
Math.sqrt(z+1). It is an expression, so its result is what gets passed, not the text of the expression. - 4Position 3
The third actual parameter is z >= 10. It is a comparison, so the value passed is either true or false.
- 5Keep the order
The subroutine receives these three values in the order they appear in the call. Changing the order changes which value lands in which formal parameter.
Three actual parameters: a plain value 17 in position 1, an expression Math.sqrt(z+1) in position 2, and a comparison z >= 10 in position 3.
The definition of doTask is not part of the source pack, so its formal parameter names are not given here. What you can rely on is their role: each formal parameter is a placeholder inside the definition that stands for a value supplied later by whoever calls the subroutine. If you need to name them in your own code, you write a header in the definition, as the next illustration shows.
- This header is a generated illustration, not the course's
doTaskdefinition - a, b, and c are formal parameters: declared here, with no values
- The header states only the name and type of each slot
Matching Values to Slots by Position
Matching between actual and formal parameters follows position. The first actual parameter feeds the first formal parameter, the second feeds the second, and so on. The name of the caller's value plays no part in the matching. Only the order matters. The diagram below traces the doTask call into the three formal slots.
Notice what the figure does not show. Nothing flows from the subroutine back to the call, and nothing asks for input. The values start in the call statement and end up in the formal parameters. If you swapped the first two actual parameters in the call, the value of Math.sqrt(z+1) would land in the first formal slot and the value 17 would land in the second, which is a different meaning for the same subroutine.
The Order of Events During a Call
Executing the call statement has essentially the same effect as a block of statements that does its work in a fixed order. First, memory locations are allocated for the formal parameters. Then the first actual value is assigned to the first formal parameter. The square root expression is computed and its result assigned to the second formal parameter. The comparison is evaluated and its true or false result assigned to the third. Only after that does the task itself run. The sequence diagram lays out this progression.
void doTask(int a, double b, boolean c) {
// formal parameters a, b, c already hold the caller's values here
System.out.println(a + " " + b + " " + c);
}- 1The header declares a, b, and c. It contains no values.
- 2By the time the first line of the body runs, the caller's values are already in a, b, and c.
- 3The body therefore reads the parameters directly and never assigns to them or asks for input.
- Illustrative generated body, not the course's
doTaskdefinition
The source is careful about the status of this block-of-statements picture. It is a model of what happens, not a complete description of the machinery. Writing the call as a list of declarations and assignments gives the same overall effect as the one-line call, but the two are not identical in every respect. Use the model to reason about order and meaning, not to predict low-level details.
Mistakes Beginners Make With Parameters
Common mistake Assigning values to the formal parameters at the top of the body
What it looks likevoid doTask(int a, double b, boolean c) { a = 17; b = Math.sqrt(z+1); ... }Why it failsThe caller has already supplied the values before the body runs. Overwriting them at the top discards the actual parameters and treats the header as empty slots waiting to be filled.
Do this insteadLeave the parameters alone at the top of the body and use them by name, since their values were assigned at call time.
Common mistake Making the subroutine ask the user for the parameter values
Why it failsThis assumes the subroutine must prepare its own inputs. Under the correct model the caller supplies them through the call statement, so the body never needs to request them.
Do this insteadPass the information in through the call's actual parameters, and let the body only use the formal parameters.
Common mistake Believing the call's positions do not matter because the names look right
What it looks likeSwapping the first two actual parameters of doTask(17, Math.sqrt(z+1), z >= 10)
Why it failsMatching follows position, not the look of the expression. The value 17 would land in the wrong formal slot.
Do this insteadCheck each actual parameter against the position of its matching formal parameter in the definition.
Common mistake Confusing the formal parameter, which is written in the definition, with the actual parameter, which is written at the call site
Tracing an Unexpected Result Back to a Parameter
When a result is not what you expected, the useful question is where along the chain from call to subroutine the value stopped matching your prediction. Start at the call, not inside the subroutine. Write down the value each actual parameter should have, based on your reading of the call, and compare that with what you expected. If the call's items match your expectations, the divergence lies in what the subroutine does with the values it received. If they do not match, the divergence lies in the call itself.
A colleague writes the call doTask(z >= 10, 17, Math.sqrt(z+1)) and gets a wrong result. Without looking inside doTask, identify which actual parameter lands in which formal position, and state which position now holds a value of a different kind than the one the original call intended.
Hints
- Number the actual parameters from left to right, starting at position 1.
- Compare each position's value kind with the original call, where position 1 was a plain value and position 3 was a comparison.
- The subroutine receives the values in the order they appear in the call.
Key Takeaways
- Formal parameters are the variables declared in a subroutine's parameter list or function definition, and actual parameters are the values or expressions supplied in the call statement.
- The computer assigns actual parameter values to formal parameters before the subroutine body runs, so the body never needs to set or request them.
- Matching between actual and formal parameters follows position, and expressions in the call are evaluated before their results are assigned.
- In
doTask(17, Math.sqrt(z+1), z >= 10), the three actual parameters are a plain value, an expression, and a comparison, each supplied in a fixed position. - When a result is unexpected, check each actual parameter in its position in the call before looking inside the subroutine.