skip to content

Predict the output of a small hierarchy that mixes an overridden method with a hidden field, and explain each result.

level: seniorimportance: should knowfreq 40%

answer

  1. Method call → runtime type; field read → declared type
  2. Cast changes the expression's compile-time type, not the object
  3. Overridden method reads its own (declaring class's) field
  4. Inherited parent method reads the parent's hidden field
  5. Getters restore intuitive (dynamic) behavior

basics

~20 s

The overridden method runs the subclass version (chosen by the real object type). The hidden field is read from whichever reference type you use (chosen at compile time). So through a parent reference, you get the child's method but the parent's field value.

solid answer

~50 s

This is the classic trap that shows methods and fields resolve differently. Through a `Parent` reference pointing at a `Child`, an overridden instance method runs `Child`'s version because methods dispatch on the object's runtime type. But a hidden field read through that same `Parent` reference returns `Parent`'s field because fields bind statically to the reference's declared type. If `Child`'s overridden method reads the field by simple name, it sees `Child`'s field (the method's declaring class is `Child`); but a method inherited from `Parent` that reads the same name sees `Parent`'s field. So you can observe the child's method printing the child's field value, while direct field access through the parent reference prints the parent's value — all on one object. The lesson: instance methods are polymorphic, fields are not, and mixing both is a readability hazard fixed by encapsulating fields behind getters.

go deeper

for a junior

Likely predicts both the method and field follow the object type; this question is a good stretch to reveal the field/method split.

for a middle

Gets the overridden-method result and usually the basic field result, but may stumble on the cast case or the inherited-method-reads-parent-field subtlety.

for a senior

Predicts all three results, explains declaring-class field binding, and proposes the getter-based fix.

for a principal

Uses this asymmetry to justify encapsulation standards (no public mutable fields, accessors only), and reasons about how hidden fields interact with serialization, reflection, and subclass evolution in libraries.

## The two resolution rules, side by side Keep one rule in mind: **instance methods dispatch by the object's runtime type (dynamic binding); fields bind by the expression's compile-time type (static binding).** Every result below follows from that. ### Definitions recap - **Runtime (dynamic) type:** the actual class of the object (`new Child()` ⇒ `Child`). - **Compile-time (declared) type:** the type written for the reference (`Parent p` ⇒ `Parent`). - **Override:** subclass replaces an instance method body; selected dynamically. - **Hide (field):** subclass declares a same-named field; both fields coexist, selected statically. - **Declaring class of a method:** the class in whose body the method's code is written; an unqualified field name inside it binds to that class's field. ### The example ```java class Parent { String label = "P"; String describe() { return "Parent sees " + label; } } class Child extends Parent { String label = "C"; @Override String describe() { return "Child sees " + label; } } Parent p = new Child(); System.out.println(p.describe()); // (1) System.out.println(p.label); // (2) System.out.println(((Child) p).label); // (3) ``` ### Result (1): `p.describe()` → `Child sees C` `describe()` is an instance method, so it is **overridden** and dispatched by the **runtime type** `Child`. `Child.describe()` runs. Inside `Child.describe()`, the unqualified `label` binds to the field of its **declaring class** `Child`, which is `"C"`. So: `Child sees C`. ### Result (2): `p.label` → `P` `label` is a **field**, accessed through an expression whose **compile-time type** is `Parent`. Field access uses **static binding**, so `Parent.label` (`"P"`) is read — even though the object is a `Child` that also holds a `label = "C"`. So: `P`. ### Result (3): `((Child) p).label` → `C` The cast changes the **compile-time type** of the expression to `Child` (it does not change the object). Static binding now selects `Child.label` = `"C"`. So: `C`. ### What it reveals One object, accessed two ways, yields two field values (`P` and `C`) but only one method behavior (the child's). That asymmetry is the whole point: **methods are polymorphic, fields are not.** If you expected `p.label` to give `"C"`, you were (reasonably) expecting field polymorphism that Java does not provide. ### A subtler variant: inherited method reading the field If `describe()` were defined **only** in `Parent` (not overridden) and read `label`, then calling it on a `Child` prints `Parent sees P` — because the running code's **declaring class** is `Parent`, so its unqualified `label` is `Parent.label`, regardless of the runtime object. This is how a hidden field can silently feed stale/parent values into inherited logic. ### How to avoid the trap Make fields **private** and read them through **getters**. A `getLabel()` overridden in `Child` dispatches dynamically, so `p.getLabel()` returns `"C"` — the intuitive result — and private fields cannot be shadowed in a confusing way. This is the standard encapsulation fix and the reason 'expose state via accessors, not public fields' is good practice. ### Summary rule > **Override the method ⇒ runtime type wins. Read the field ⇒ declared type wins (and a cast switches which hidden field you see).**

  • If describe() existed only in Parent and read label, what does a Child instance print and why?
    'Parent sees P'. The executing code's declaring class is Parent, so its unqualified label binds to Parent.label — the runtime object being a Child with its own hidden label doesn't change the field binding.
  • How would you rewrite the classes so p reading the state gives the child's value?
    Make label private and add a getLabel() that Child overrides to return its own value. Method calls dispatch dynamically, so p.getLabel() returns the child's value; private fields also prevent the confusing shadowing.

saying these in an interview costs you the question

  • Predicting p.label gives the child's value
  • Thinking the cast mutates the object's data
  • Assuming an inherited parent method sees the child's hidden field
  • Conflating which-method (dynamic) with which-field (static) resolution

context