Inheritance Hierarchy and Enclosing Context – Nested Type Declarations 02/22/2023 Rivka Scheinberg Post in Garbage Collection,Implementing an Interface,Oracle Exams Inheritance Hierarchy and Enclosing Context A non-static member class can extend another class and implement interfaces, as any normal class. An inherited field (or method) in a non-static member subclass can hide a field (or method) with the same name in the enclosing context. Using the simple name to access this member will access the inherited member, not the one in the enclosing context. Example 9.7 illustrates the situation. In the inner subclass at (4), the field name value at (1) in the superclass hides the field with the same name in the enclosing class at (3). In Example 9.7, the standard form of the this reference is used to access the inherited field value, as shown at (6). The simple name of the field would also work in this case, as would the keyword super with the simple name. The super keyword would be mandatory to access the superclass field if the inner subclass also declared a field with the same name. However, to access the member from the enclosing context, the qualified this must be used, as shown at (7). Example 9.7 Inheritance Hierarchy and Enclosing Context Click here to view code image // File: HiddenAndInheritedAccess.javaclass Superclass { protected int value = 3; // (1) Instance field in superclass}//_____________________________________________________________________________class TopLevelClass { // (2) Top-level Class private double value = 3.14; // (3) Hidden by the instance field // at (1) in the inner subclassclass InnerSubclass extends Superclass { // (4) Non-static member subclass public void printHidden() { // (5) // (6) value from superclass: System.out.println(“this.value: ” + this.value); // (7) value from enclosing context: System.out.println(“TopLevelClass.this.value: “ + TopLevelClass.this.value); } } // InnerSubclass} // TopLevelClass//_____________________________________________________________________________public class HiddenAndInheritedAccess { public static void main(String[] args) { TopLevelClass.InnerSubclass ref = new TopLevelClass().new InnerSubclass(); ref.printHidden(); }} Output from the program: Click here to view code image this.value: 3TopLevelClass.this.value: 3.14 Some caution should be exercised when extending an inner class. Some of the subtleties involved are illustrated by Example 9.8. The nesting and the inheritance hierarchy of the classes involved are shown in Figure 9.4. The question that arises is how do we provide an outer instance when creating a subclass instance of a non-static member class—for example, when creating objects of the subclasses SubInnerA and InnerB in Figure 9.4. Figure 9.4 Non-Static Member Classes and Inheritance Example 9.8 Extending Inner Classes Click here to view code image // File: Extending.javaclass OuterA { // (1) class InnerA { } // (2)}//_____________________________________________________________________________class SubInnerA extends OuterA.InnerA { // (3) Extends NSMC at (2)// (4) Mandatory non-zero argument constructor: SubInnerA(OuterA outerRef) { outerRef.super(); // (5) Explicit super() call }}//_____________________________________________________________________________class OuterB extends OuterA { // (6) Extends class at (1) class InnerB extends OuterA.InnerA { } // (7) Extends NSMC at (2)}//_____________________________________________________________________________public class Extending { public static void main(String[] args) { // (8) Outer instance passed explicitly in constructor call: SubInnerA obj1 = new SubInnerA(new OuterA()); System.out.println(obj1.getClass()); // (9) No outer instance passed explicitly in constructor call to InnerB: OuterB.InnerB obj2 = new OuterB().new InnerB(); System.out.println(obj2.getClass()); }} Output from the program: class SubInnerAclass OuterB$InnerB In Example 9.8, the non-static member class InnerA, declared at (2) in the class OuterA at (1), is extended by SubInnerA at (3). Note that SubInnerA and the class OuterA are not related in any way, and that the subclass OuterB inherits the class InnerA from its superclass OuterA. An instance of SubInnerA is created at (8). An instance of the class OuterA is explicitly passed as an argument in the constructor call to SubInnerA. The constructor at (4) for SubInnerA has a special super() call in its body at (5), called a qualified superclass constructor invocation. This call ensures that the constructor of the superclass InnerA has an outer object (denoted by the reference outerRef) to bind to. Using the standard super() call in the subclass constructor is not adequate because it does not provide an outer instance for the superclass constructor to bind to. The non-zero argument constructor at (4) and the outer-Ref.super() expression at (5) are mandatory to set up the relationships correctly between the objects involved. The outer object problem mentioned above does not arise if the subclass that extends an inner class is also declared within an outer class that extends the outer class of the superclass. This situation is illustrated at (6) and (7): The classes InnerB and OuterB extend the classes InnerA and OuterA, respectively. The member class InnerA is inherited by the class OuterB from its superclass OuterA—and can be regarded as being nested in the class OuterB. Thus an object of class OuterB can act as an outer object for both an instance of class InnerA and that of class InnerB. The object creation expression new OuterB().new InnerB() at (9) creates an OuterB object and implicitly passes its reference to the default constructor of class InnerB. The default constructor of class InnerB invokes the default constructor of its superclass InnerA by calling super() and passing it the reference of the OuterB object, which the constructor of class InnerA can readily bind to. It goes without saying that such convoluted inheritance and nesting relationships as those in Example 9.8 hardly qualify as best coding practices.