3.3. Variable Scoping¶
3.3.1. Variable Scoping¶
This section discusses exactly where a user-defined name can be accessed. The scope of a user-defined name is the region of source code where the name is visible and can be used.
3.3.1.1. Local Scope¶
Recall that mutator methods (or “setters” as they’re often called) are used to change the values of private fields in a class. For example:
public class Cat
{
private String color;
// ...
public void setColor(String newColor)
{
color = newColor;
}
}
This setColor()
method makes use of both a field and a parameter.
It is important to note that there is a difference in where these two types
of variables can be used. The scope of a variable or method refers to where
it can be used in a program.
A parameter’s scope is limited to the body of the method in which it is
declared. Parameters are local variables
which are declared in the parameter list of a method’s header and which
have initial values specified by the arguments in a method call. For example,
if we had an object of type Cat
called c
, we could call
the setColor()
method like so:
c.setColor("Black");
When we write the method, we declare the variable newColor
, and when we call
the method here,
we set s
equal to the string “Black”. If we wanted to call the method
again, we would need to provide a new value for the variable newColor
.
c.setColor("Black");
c.setColor("Grey");
Such values do not carry over between method calls.
The scope of a parameter is the same as the scope of a variable declared at the very beginning of the body of a method. Once the flow of execution leaves a method, its parameters and other local variables cease to exist. The scope of local variables is referred to as local scope.
Local Variables. Local variables, that is, parameters and variables declared in the body of a method, have local scope which extends from the point at which they are defined to the end of the block of code in which they are defined. In particular, the scope of a parameter is the entire body of the method in which it is declared.
It would be a syntax error to refer to a method’s parameters or other local variables from outside the method.
3.3.1.2. Block-Level Scope¶
Variables that are declared in the body of a method have scope which extends from the point where they are declared to the end of the block of code in which they are declared. When a local variable is declared at the beginning of a method, it has the local scope discussed above.
However, local variables are not restricted to the beginning of a method, and their declarations can be placed elsewhere, which can affect their scope. When control structures like if-statements or loops are involved, scope can be a bit more specific.
public void exampleMethod(int x)
{
if (x % 2 == 0)
{
int value = 4;
}
value = value + 2; // This will not work!
}
Variables declared inside the curly braces ({}
) of a control structure
like a loop or conditional only exist within those curly braces. The method
above would not work as the variable value
is only declared and initialized
inside the if statement’s true branch, and that variable ceases to exist when
the corresponding closing brace marking the end of the if statement’s true
branch is reached. As a result, its name is no longer visible once execution
has left the block (the pair of braces) where it is declared. Any attempt to
use the variable outside of the braces where it is declared will result in a
compiler error, since the variable is no longer visible or accessible–no longer
“in scope”.
The same is true for looping structures:
for (int i = 0; i < 12; i++)
{
System.out.println(i);
}
i = i + 1; // This will not work!
The variable i
is defined as part of the for loop and its scope is the
body of the for loop–the braces surrounding the loop’s body. The variable i
ceases to exist after the for loop is finished.
To get around this issue, you will sometimes see code where a variable is declared before a control structure, so that it can be accessed inside the control structure and also after it.
public void exampleMethod(int x)
{
int value = 0;
if (x % 2 == 0)
{
value = 4;
}
value = value + 2;
}
We could also do something similar with a for loop:
int i = 0;
for (i = 0; i < 12; i++)
{
System.out.println(i);
}
i = i + 1; // This will work!
3.3.1.3. Class Scope¶
By contrast, fields and all methods have scope that extends throughout the entire class, that is, class scope. They can be used in the body of any method and in the expressions that assign initial values to class level variables.
Class-Level Variables. Fields and methods have class scope, which extends throughout the class.
3.3.1.4. A Common Misconception¶
After declaring a variable it is tempting to use to both the variable name and the variable’s type whenever referring to it. For example:
public class Cat
{
private String color;
// ...
public void setColor(String newColor)
{
String color = newColor;
}
}
This setter will not change the value of the field color
. To Java,
whenever the type of a variable is included, you are declaring a variable.
Java will allow you to declare variables with the same name, as long as they
are in different scopes.
In the code above, there is a field called color
with class-level
scope, and a local variable called color
that only exists within
the setColor()
method.
Even though these variables have the same name and type, they are different. Changing one will not change the other.
3.3.1.5. A Note on Naming¶
As we saw above, Java can handle having two variables with the same name
and type when they are declared in different scopes. This can also lead to
confusion. For example, we could
have two String variables called color
. One a field and one a parameter.
public class Cat
{
private String color;
// ...
public void setColor(String color)
{
color = color;
}
}
This code would compile but it is not advisable to use such naming conventions.
This is because it is not clear if the field color
is being set to the
parameter color
or vice-versa, or something else entirely. Let’s take a
look at what is happening here by adding a few print statements:
public class Cat
{
private String color;
public Cat()
{
this.color = "Black";
}
public void setColor(String color)
{
color = color;
System.out.println(this.color);
System.out.println(color);
}
public static void main(String[] args) {
Cat cat = new Cat();
cat.setColor("Green");
}
}
In this example, whenever we make a new Cat
object, the value of the
field color
is set to “Black” at first. When we run the main method with setColor("Green")
we see an interesting result in our print statements:
$ javac Cat.java
$ java Cat
Black
Green
The first thing to be printed out is this.color
. Which we see is “Black”.
The value of the field was not changed to “Green”! This means that when we write
color = color
we know that the field color was not on the left side of
the assignment operator.
One might assume, then, that the parameter color
is the value on the left
side of the assignment operator. This would mean that the parameter was changed
from “Green” to “Black”. But our second print statement tells us otherwise.
When we print out the parameter color
we see it is still “Green”. This
means that the field color
was not on the right side of the equals
sign either!
What happened in this code is that we set the parameter variable color
equal to itself–meaning nothing changed!
Generally, the best way to avoid such confusion is to give your variables distinct names like we did initially:
public void setColor(String newColor)
{
color = newColor;
}
Alternately, if for some reason you must use the same variable name at two
different scope levels, using the modifier this
will help clarify which
variable you are referring to:
public void setColor(String color)
{
this.color = color;
}
Now, the field color
is on the left side of the assignment operator and the
parameter color
is on the right. So, if we ran setColor("Green");
the field color
would be changed from “Black” to “Green”.
You will sometimes see this convention in setter methods or constructors,
where the programmer has intentionally used the same name for both the
parameter and the field, to communicate the intent that the parameter is
the value that will be stored in the field. When using this approach it
is mandatory to alway include this.
as a prefix when referring to the
field name, because otherwise, all uses of the name would refer to the
parameter only.
3.3.2. Summarizing Scope Concepts¶
Note
The readings for this semester sometimes have interactive widgets for you to practice concepts. These exercises are optional and are not graded, but we encourage you to try them out.
If anything is confusing or if you have questions about the exercises, we encourage you to post to Ed!