Wednesday, June 14, 2023

Quiz yourself: Delegation using super(...) and this(...) during constructor execution

Oracle Java Certification, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Prep, Oracle Java Preparation, Oracle Java Tutorial and Materials, Oracle Java Learning

There are rules for explicit constructor invocations. Do you know them?


Given the following two classes

class Building {
  Building nextBuilding;
  Building() {}
  Building(Building nb) {
    nextBuilding = nb;
  }
}

class Hotel extends Building {
  final Building nb = new Building();
}

Which constructor can be added to the Hotel class without causing compilation errors? Choose two.

A. Hotel() { super(nb);  }
B. Hotel() { super(nextBuilding);  }
C. Hotel() { super(super);  }
D. Hotel() { super(this);  }
E. Hotel() { super(null);  }
F. Hotel() { super(new Hotel() {});  }

Answer. One conceptual model of the relationship between the parent type aspects and the subtype aspects of an object is to consider the parent type aspects as the foundation upon which the subtype parts are built, rather like putting a house on a foundation and then putting a roof on that house. Clearly, if you try to work on the roof before you build the walls or you work on the walls before preparing the foundation, you will have a problem.

Java tries hard to enforce this notion. The delegation behavior of super(...) and this(...) during constructor execution is such that it mostly enforces the initialization of an object to start from Object and work its way from the parent aspect to the child aspect. Somewhat simplified, the order of operations is as follows:

1. Invocation of new allocates and clears memory for the entire object’s storage and passes the reference to that storage as the implicit this argument into the matching constructor.

2. Calls to this(...) or super(...)—which appear, or can be implicit, as the first statement in a constructor—force execution up to the constructor for Object, which does not have a call to super() since it has no superclass.

3. Upon returning from each super(...) constructor, the instance initializers of the current class are executed, and then these are followed by the body of the constructor(s) at the current level.

4. After the current level finishes processing, control returns to the next subclass level down.

Given this outline, consider what would happen if calls to this(...) or super(...) could refer to any instance aspect of the object being initialized. Such an aspect would still be uninitialized. That’s not a very smart way to go about preparing a well-built object: It’s like trying to paint the roof before the walls are in place.

Here are two things you should know.

◉ A call to this(...) or super(...) must be the first statement in the constructor—and super() will be added implicitly if neither this nor super is coded.
◉ The argument list to these calls must not refer to this either explicitly or indirectly by an unqualified identifier that would be resolved using an implicit this reference. The super prefix, which is essentially a reference to this but with a different type, is also prohibited.


An explicit constructor invocation statement introduces a static context (§8.1.3), which limits the use of constructs that refer to the current object. Notably, the keywords this and super are prohibited in a static context (§15.8.3, §15.11.2), as are unqualified references to instance variables, instance methods, and type parameters of lexically enclosing declarations (§6.5.5.1, §6.5.6.1, §15.12.3).

In view of the restriction just described, consider the quiz options.

In option A, the actual parameter to the super call is nb. This can be resolved only as an implicit reference to this.nb and, as such, the constructor is not valid and option A is incorrect.

Option B fails by the same reasoning because the identifier nextBuilding is resolved as this.nextBuilding and is not valid as an argument to the super call.

Option C is incorrect for two reasons. First, super is not usable as a standalone reference; it can be used only as a prefix. Second, as already mentioned, super can’t appear in the argument list of this(...) or super(...).

Option D is similarly incorrect because this cannot appear in the argument list of this(...) or super(...).

Option E is correct. While it might not be helpful from a semantic perspective, null is a valid parameter to pass to this(...) or super(...) provided there is a target constructor that takes an argument of reference type.

Option F is also correct because it shows valid syntax constructing an anonymous subclass of Hotel. Such an expression is acceptable as an argument to the super(...) invocation and will delegate to the Building constructor of the form Building(Building nb).

As a side note, it has always been permitted to place expressions as actual parameters to this(...) and super(...) calls, provided they make no direct or indirect reference to this. Such expressions can make unrestricted use of static members (methods and fields) of the class being initialized or other classes.

However, as of Java 17, it’s not permitted to put any code prior to the statement containing this(...) or super(...), and an implicit super() will always precede any code we write ourselves.

At this time, however, there is a JEP preview that proposes to allow code before a this() or super() call, which might allow tidier preparation of arguments.

Conclusion. The correct answers are options E and F.

Source: oracle.com

Related Posts

0 comments:

Post a Comment