Report Hierarchy: Constructor Dispatch, Hidden Statics and Field Shadowing
A reporting module contains the following classes. Report declares int pages = 10;, a private String format() returning "plain", a protected Object summarize() returning "summary", a static void render() printing "Report ", a no-argument constructor that calls describe(), and an instance method void describe() printing "Report.describe ". Summary extends Report declares int pages = 20;, a public String format() returning "fancy", an @Override public String summarize() returning "brief", a static void render() printing "Summary ", and @Override void describe() printing "Summary.describe " followed by pages. A test does Report r = new Summary(); System.out.println(r.pages); r.summarize(); r.render();. The output surprises the team: Summary.describe 0 appears before anything else. A reviewer also claims format() is overridden and that r.render() must print "Summary " because the object is a Summary.
1. Explain the very first line of output produced by `new Summary()` and why `pages` shows 0.
Read the worked answer
The first output line is Summary.describe 0 and it comes from Report's constructor.
Execution order for new Summary():
Summary()implicitly callssuper(), soReport's constructor body runs first.Report's constructor callsdescribe(). Method selection uses the runtime type of the object, which is alreadySummary, soSummary.describe()executes even thoughReport's constructor is still running.- At that moment the instance fields of
Summaryhave not been initialised yet - field initialisers run after the superclass constructor returns. Sopagesstill holds its default value 0, not 20.
Printing the summary reveals Summary.describe 0. Only after the superclass constructor finishes does int pages = 20; execute for Summary, and then the Summary constructor body runs.
The lesson: never call an overridable method from a constructor; a subclass override may see partially constructed state (0, false, null). Making describe() final, private or static removes the hazard, but private/static also change which method is invoked.
2. Classify `format()`, `summarize()` and `render()` in `Summary` as override, hide, or new method, and state which visibility and return-type changes are legal.
Read the worked answer
format()- new method, not an override.Report.format()isprivate, so it is not inherited and cannot be overridden.Summary.format()merely declares an unrelated method that happens to share a signature;@Overrideon it would be a compile error.Reportcode callingformat()always gets "plain";Summarycode gets "fancy".summarize()- a legal override. It keeps the same parameter list, widens access fromprotectedtopublic(widening is allowed; narrowing toprivate/package-private would fail), and narrows the return type fromObjecttoString. A covariant return is allowed when the new type is a subtype of the old one;Stringis a subtype ofObject, so this compiles.render()- hiding, not overriding. It isstatic. A static method with the same signature in a subclass hides the superclass version; it is bound at compile time using the reference type, so@Overrideis illegal on it (though@Overrideon a static method that re-declares nothing is simply an error).
Summary of rules tested: an override must not be more restrictive in access, must not throw broader checked exceptions, must have a compatible (covariant) return type, and cannot turn an instance method into a static one or vice versa. Constructors are never overridden, and final methods cannot be overridden at all.
3. What does `r.pages` print and what does `r.render()` print, given `Report r = new Summary();`? Why do these differ from `r.summarize()` and `r.describe()`?
Read the worked answer
r.pages prints 10 and r.render() prints **Report **.
Fields: field access is resolved at compile time from the type of the reference, not the object. r is declared Report, so r.pages reads Report's field (10). ((Summary) r).pages would read 20. The two fields are independent storage slots - the subclass field shadows the superclass field rather than replacing it. This is why fields should be private and accessed through methods.
Statics: render() is static, so it is bound from the reference type: Report.render(). Calling a static method through an instance reference is legal but discouraged; Summary.render() would require a Summary reference or the class name.
Instance methods: summarize() and describe() are virtual. The JVM dispatches on the object's actual class (Summary), so the overriding versions run and print "brief" and "Summary.describe 20 ". The describe() call from inside the constructor was different only because of timing, not because dispatch rules changed.
Rule to memorise: instance methods are polymorphic; fields and static methods are not.