skip to content

How does Java decide which variable a bare name refers to when a field and a local of the same name both exist?

level: middleimportance: must knowfreq 55%

answer

  1. Innermost-first, first match wins
  2. Local > field > inherited field
  3. Lexical, resolved at compile time
  4. this. / super. / Class. as qualifiers
  5. Local cannot shadow local in overlapping scope

basics

~20 s

Java looks at the closest (innermost) scope first. If a local variable or parameter has the name, that wins; the field is only used if no closer variable has the name. Use this.name to force the field.

solid answer

~40 s

Java resolves an unqualified variable name by walking outward from the innermost enclosing scope and binding to the first declaration it finds. So a parameter or local variable in the current block takes precedence over a field of the same name; the field takes precedence over an inherited field. This is purely lexical and decided at compile time, not at runtime. Because the closest declaration wins, a same-named local shadows the field for the rest of that scope. To bypass shadowing you qualify the name: this.name reaches the current object's field, and super.name reaches an inherited field. Note that locals cannot shadow other locals in overlapping scopes, since redeclaring a local name in a nested block that already has it in scope is a compile error.

go deeper

for a junior

Knows that a local with the same name as a field takes precedence and that this.field reaches the field.

for a middle

States the full innermost-first order (local > field > inherited) and that it is lexical/compile-time.

for a senior

Contrasts lexical name resolution with runtime overriding, knows the local-vs-local compile error, and uses super./Class. qualifiers correctly.

for a principal

Explains scope resolution as a language-design property, relates it to field hiding and dispatch, and reasons about implications for readable APIs and tooling.

## The search order When the compiler sees a bare identifier used as a variable, it must decide which declaration it names. Java uses **lexical (static) scoping**: it searches enclosing scopes from the inside out and binds to the **first** matching declaration. The practical precedence is: 1. **Local variables and parameters** in the current block (innermost wins). 2. **Fields of the current class** (instance or static). 3. **Inherited fields** from superclasses/interfaces. This is resolved entirely **at compile time** based on where the code is written — not at runtime, and not by the dynamic type of any object. (Contrast this with method *overriding*, which is dispatched at runtime by the object's actual type.) ## Why a local shadows a field Because locals are searched before fields, a local or parameter with the same name as a field **shadows** that field within its scope. The field is not gone; it is just unreachable by the plain name: ```java class Counter { int value = 10; // field int demo() { int value = 3; // local shadows the field return value; // 3 (the local) } int field() { int value = 3; return this.value; // 10 (the field, via this) } } ``` ## Qualifiers that bypass shadowing - **`this.name`** — names the field `name` of the current object, skipping any local/parameter. - **`super.name`** — names an inherited field `name` from the superclass (relevant to *field hiding*, where a subclass field hides a superclass field). - **`ClassName.name`** — names a static field, skipping locals. ## The local-vs-local rule Unlike some languages, Java forbids a local from shadowing another local that is already in scope in an enclosing block: ```java int x = 1; { int x = 2; } // COMPILE ERROR: x is already defined ``` So shadowing in Java is always between a **local/parameter and a field**, or between fields across the inheritance chain — never between two locals on the same nested-block path. (Two locals in *disjoint* sibling blocks may reuse a name, but that is not shadowing because their scopes never overlap.) ## Static vs runtime Key takeaway: which variable a name binds to is fixed when the code compiles. Reassigning references or subclassing does not change a local-vs-field resolution. This determinism is what makes `this.field` a reliable escape hatch.

  • Is name resolution for shadowing done at compile time or runtime?
    At compile time. Java uses lexical scoping, so the binding is fixed by where the code is written, unlike method overriding which dispatches at runtime.
  • What is the precedence between a local, a class field, and an inherited field of the same name?
    Local/parameter beats class field, and a class field beats an inherited field. You reach the others with this.x (own field) or super.x (inherited).

saying these in an interview costs you the question

  • Claiming resolution depends on the runtime type of the object
  • Saying the field wins over a local (it is the opposite)
  • Confusing this lexical rule with dynamic method dispatch
  • Assuming two locals can shadow in overlapping scopes

context