All topics
library
beginner

Variable Scoping & Shadowing

Know how Java resolves variable names when local, instance, and class-level variables share the same name.

A variable is visible from its declaration to the end of its enclosing block. When two names collide, the innermost wins — that is shadowing.

Like nested Russian dolls: open the smallest one first (local scope). If the name isn't there, open the next bigger one (instance scope), then the next (class scope).

Key Concepts

1
    class Counter {
      int count = 10;                  // field
2
      void bump(int count) {           // parameter shadows the field
        count++;                       // the PARAMETER -- field unchanged
        this.count++;                  // the field
      }
    }
3
That is the bug. count++ looks like it updates the object and does not. The field needs this..
count++this.
4
The scopes, innermost outward.
    block      a for variable, a catch parameter
    method     parameters and locals
    instance   fields, reachable as this.x
    class      static fields, reachable as ClassName.x
5
You cannot redeclare a local in an overlapping scope.
    int x = 1;
    { int x = 2; }                     // will not compile
6
Which differs from C, and from field shadowing, which is allowed.
7
A lambda cannot shadow an enclosing local either:
    int x = 1;
    run(y -> { int x = 2; });          // will not compile
8
Lambdas share the enclosing scope rather than creating a nested one, unlike an anonymous class, which can shadow.
9
Block scoping is tighter than people expect.
    for (int i = 0; i < 10; i++) { }
    print(i);                          // will not compile
10
The same applies to try-with-resources variables and, since Java 14, to a pattern variable in a switch case, which is scoped to its own branch.
11
A static field shadowed by an instance field needs ClassName.field to reach. It is legal and almost always a mistake.
ClassName.field
12
The practical rule. Name things so shadowing never arises. this.name = name in a setter is the one accepted exception, because the parameter name matching the field is clearer than inventing newName.
this.name = namenewName
13
What the interviewer is probing.1. "What is shadowing and where is it acceptable?" Probing: whether you treat it as a rule or a judgement. Stalls: "It is always bad." Moves up: an inner name hiding an outer one; the accepted case is a setter parameter matching the field, resolved with this.
14
2. "Can a lambda declare a variable with the same name as an enclosing local?" Probing: how lambdas scope compared with anonymous classes. Stalls: "Yes, it is a new scope." Moves up: no — a lambda shares the enclosing scope, so it does not compile, while an anonymous class can shadow.
15
3. "Why does a for-loop variable disappear after the loop?" Probing: block scoping. Stalls: "It is cleaned up." Moves up: it is scoped to the loop's block, so referencing it afterwards does not compile — the same applies to try-with-resources and switch pattern variables.
16
4. "Where do you have to qualify a name to reach what it shadows?" Probing: the qualifiers. Stalls: "You cannot." Moves up: this.field for an instance field, ClassName.field for a static one.