skip to content

If a subclass declares a field with the same name as its superclass, how is that field resolved when accessed through different reference types?

level: seniorimportance: should knowfreq 48%

answer

  1. Fields resolve by reference type, not object type
  2. Cast changes which hidden field you read, not the object
  3. Both Parent and Child fields coexist in one object
  4. Method reads its own declaring class's field
  5. Fix: private fields + overridable getters

basics

~20 s

Fields are never overridden, only hidden. Java picks the field based on the type of the reference you use, decided at compile time — not the actual object. So the same object can hold both the parent's and child's field, and a cast changes which one you read.

solid answer

~50 s

Fields use static binding: the field accessed is chosen by the compile-time (declared) type of the reference expression, not the runtime type of the object. When a subclass declares a field with the same name as the superclass, it *hides* the superclass field rather than replacing it — both fields physically exist in the object at the same time. So `Child c = new Child(); Parent p = c;` means `c.x` reads Child's x while `p.x` (or `((Parent)c).x`) reads Parent's x, all on one object. Inside a method, `this.x` resolves to the field visible in that method's declaring class, and `super.x` reaches the parent's. This is a common gotcha because it looks like polymorphism but isn't. The fix is to make fields private and expose them through getters, which are instance methods and therefore dispatch dynamically — restoring the override behavior people expect.

go deeper

for a junior

May not realize fields can be hidden at all; typically assumes fields behave like overridden methods.

for a middle

Knows fields aren't overridden but may be fuzzy on how casts and reference types select the hidden field.

for a senior

Predicts the output of cast/upcast field reads, explains static binding, and knows the private-field-plus-getter remedy and the this/super resolution rule.

for a principal

Treats field shadowing as an anti-pattern in API design, enforces encapsulation conventions, and anticipates how hiding interacts with serialization, frameworks reflecting over fields, and subclass evolution.

## Setup: what 'field hiding' means A **field** is a variable declared in a class body (instance data). When `Child extends Parent` and both declare a field with the same name, the subclass field **hides** (shadows) the superclass field. Crucially, hiding does **not** remove or replace the parent's field — **both fields exist in the same object simultaneously**, occupying separate slots. Contrast with **overriding**, which applies only to *instance methods* and replaces behavior via dynamic dispatch. Fields have **no** polymorphism. ### Static vs dynamic binding (the key mechanism) - **Compile-time type (declared/static type):** the type in the variable declaration. In `Parent p = child;`, it's `Parent`. - **Runtime type (dynamic/actual type):** the class of the object created — `Child`. - **Static binding:** the compiler resolves the reference at compile time using the compile-time type. - **Dynamic binding:** the JVM resolves at run time using the runtime type. **Field access uses static binding.** The field you get is decided entirely by the **compile-time type of the access expression**, never the object's runtime type. ### Worked example ```java class Parent { String tag = "P"; } class Child extends Parent { String tag = "C"; } Child c = new Child(); Parent p = c; // same object, two reference types System.out.println(c.tag); // "C" (expression type Child) System.out.println(p.tag); // "P" (expression type Parent) System.out.println(((Parent) c).tag); // "P" (cast changes the expression type) ``` All three lines touch **one** object, yet print different values, because the *reference type* selects the field. A cast to `Parent` doesn't change the object — it changes the compile-time type of the expression, and therefore which hidden field is read. ### this and super inside methods Inside a method, an unqualified field name resolves to the field visible in the **method's declaring class**. So a method *defined in Parent* that reads `tag` reads `Parent.tag`, even when invoked on a `Child` — because the method's declaring class is `Parent`. `super.tag` from `Child` reaches `Parent.tag`; `this.tag` in `Child` reaches `Child.tag`. This is why a parent method that reads a parent field can silently see the *parent's* copy while the child has its own copy with a different value — a classic source of confusion. ### Why this is a trap The code *looks* like overriding, so readers expect the runtime type to win. It doesn't. The bug surfaces when you upcast (e.g. store objects in a `List<Parent>`) and read a field — you get the parent's value, not the child's. ### The remedy - Don't shadow fields. Use a different name, or don't redeclare at all. - Make fields **private** and expose them through **getters**. Getters are instance methods, so they **override** and dispatch dynamically — `p.getTag()` on a `Child` correctly returns the child's value. This is the standard encapsulation fix that also sidesteps the hiding hazard. ### Summary rule > **Fields → resolved by the declared (compile-time) type. Methods → resolved by the runtime type.** A cast or differently-typed reference changes which hidden field you see; it never changes the object.

  • Why do getters fix the field-hiding surprise?
    Getters are instance methods, so calling getTag() on a Parent reference dynamically dispatches to Child's override and returns the child's value — the polymorphism people expected from the field. Encapsulating fields as private removes the ability to shadow them at all.
  • Inside a method declared in Parent that reads field x, which x does it see when called on a Child that hides x?
    Parent's x. Unqualified field access binds to the field of the method's declaring class (Parent), so the inherited method sees the parent's copy even though the object is a Child with its own x.

saying these in an interview costs you the question

  • Expecting field access to be polymorphic by runtime type
  • Thinking a cast mutates or replaces the object's fields
  • Assuming the subclass field 'wins' inside an inherited parent method
  • Confusing field hiding with method overriding

context