Static Hiding, Field Shadowing and a Constructor That Prints Too Early
A team is reviewing these classes before a refactor.
class Base {
static String kind() { return "base"; }
String who() { return "Base"; }
int size = 1;
Base() { System.out.print(who() + " "); }
}
class Child extends Base {
static String kind() { return "child"; }
String who() { return "Child"; }
int size = 2;
Child() { System.out.print(size + " "); }
}
The snippet under review is Base b = new Child(); followed by System.out.print(b.kind() + b.who() + b.size);. One reviewer expects b.kind() to print child; another predicts the total output is Child 2 ChildChild1. A third would add @Override to kind() and to who().
1. State the exact console output and justify each of the four printed pieces.
Read the worked answer
Output: Child 2 baseChild1.
new Child()triggersChild's constructor, which implicitly callssuper()first.Base's constructor body runswho(). That is an instance method call, so it dispatches dynamically on the actual object type,Child- printingChild.Child's field initializerint size = 2;then runs, andChild's constructor body prints2.b.kind()is a static method. Static methods are resolved at compile time from the declared type of the reference, which isBase, sobaseis printed. No dynamic dispatch occurs.b.who()is virtual - the runtime object isChild, soChildis printed.b.sizeis a field access, resolved at compile time onBase, so1is printed. Fields are not polymorphic; there are two distinctsizevariables in the object.
2. What happens if `@Override` is added to `Child.kind()`, and why does the same annotation on `who()` cause no problem?
Read the worked answer
@Override on kind() is a compile-time error. kind() is static, and static methods are hidden, not overridden. The annotation asserts that the method overrides or implements a supertype method, and an instance method in a superclass cannot be overridden by a static method in a subclass (and vice versa). Neither can a static method override another static method - that is hiding. The compiler therefore rejects the claim.
who() is an ordinary instance method with a compatible signature and return type, so it genuinely overrides Base.who(). @Override is exactly correct there and is valuable: if Base.who() were later renamed, the annotation turns a silent behaviour change into a compiler error.
Practical rule: never place @Override on a static method; when a reviewer sees it, the intended fix is usually to make the method an instance method, or to remove the misleading annotation and call it through the class name.
3. Suppose `Child.who()` were changed to return `"Child" + size`. What would step 1 of the output become, and what general hazard does this illustrate?
Read the worked answer
It would print Child0 , because Child's field initializers run only after Base's constructor body has completed. At the moment Base() calls the overridden who(), the Child part of the object exists but int size = 2; has not executed yet, so size is still the default 0. The array of field values is defaulted before any constructor body runs.
This is the classic 'calling an overridable method from a constructor' hazard. The superclass constructor observes a half-initialized subclass: fields are default (0, false, null) and subclass invariants have not been established. A null-valued field dereferenced inside such a method causes a NullPointerException before the object is even constructed. Mitigations: keep constructors free of overridable calls, or make the method final/private, or move the work into a factory method invoked after construction completes.