Java SE 8 Programmer I (OCAJP 8) study materials
Java SE 8 Programmer I (OCAJP 8) study resources

Report Hierarchy: Constructor Dispatch, Hidden Statics and Field Shadowing

Java SE 8 Programmer I (OCAJP 8) · Put your knowledge to work in context.

All case studies

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():

  1. Summary() implicitly calls super(), so Report's constructor body runs first.
  2. Report's constructor calls describe(). Method selection uses the runtime type of the object, which is already Summary, so Summary.describe() executes even though Report's constructor is still running.
  3. At that moment the instance fields of Summary have not been initialised yet - field initialisers run after the superclass constructor returns. So pages still 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() is private, so it is not inherited and cannot be overridden. Summary.format() merely declares an unrelated method that happens to share a signature; @Override on it would be a compile error. Report code calling format() always gets "plain"; Summary code gets "fancy".
  • summarize() - a legal override. It keeps the same parameter list, widens access from protected to public (widening is allowed; narrowing to private/package-private would fail), and narrows the return type from Object to String. A covariant return is allowed when the new type is a subtype of the old one; String is a subtype of Object, so this compiles.
  • render() - hiding, not overriding. It is static. 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 @Override is illegal on it (though @Override on 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.