Variable Scope and Shadowing
Scope
Once we start using code blocks, we can have variables with different scopes in our programs. A variable's scope is the portion of the program where it exists. If we try to refer to a variable outside of its scope, we'll get an error because it doesn't exist.
When you declare a variable inside of a code block, that variable's scope is limited to the inside of that code block. Once the code block ends, the variable no longer exists. Try running the following example:
if (true) {
int text = "Hello";
println(text);
}
println(text);
We'll get an error when we try to print text
a second time because it only
exists inside the code block.
Shadowing
Shadowing occurs when we have two variables with the same name. The newer variable "shadows" the older variable, taking its place until the newer variable goes out of scope. Jshell will allow shadowing within the same scope, which permanently replaces the old variable, but in compiled Java we can only shadow when the new variable has a different scope from the original.
// this will work fine in jshell, but it won't compile as part of a Java program
int x = 5;
int x = 10; // stating the type declares a new variable named x
// the correct way to change the value of a variable from 5 to 10 is to reassign
// it, not declare the variable a second time
int y = 5;
y = 10; // reassigns the variable instead of redeclaring it
Usually it's best to avoid this, but in my experience it's common for new programmers to unnecessarily declare the same variable multiple times until they learn the difference between declaring and initializing a variable and reassigning a variable. Jshell makes this bad habit easier to form because it allows shadowing within the same scope, as shown above. Compiled Java will still allow shadowing when the scopes differ:
// shadowing
int x = 5;
if (true) {
// this is a new variable named x, and it will cease to exist at the end of
// this block of code
int x = 10;
}
// this prints the original x value of 5 because we made a new temporary x
// instead of reassigning x
println(x);
// no shadowing
int y = 5;
if (true) {
// we reassign y instead of declaring a new y with `int y = ...`
y = 10;
}
// the original y was reassigned to 10, so this will print 10
println(y);
In general, I recommend that you avoid shadowing because of the errors that it enables you to make.