Inheritance

Inheritance is the OOP property whereby a class can extend another class (treat it as a template) in order to inherit its members (incorporate them without explicit coding).  This is indicated in the class’s declaration by the keywords: extends otherclassname. The extended class is termed the direct superclass, base class, or parent class.  The extending class is termed the direct subclass, derived class, or child class.  Its own explicitly coded members supplement or override its inherited members.

A subclass may itself be extended by a further subclass.  An inheritance chain is a series of classes directly extending each other; within one, a given class is a subclass of every class above it and a superclass of every one below it.  A subclass inherits every member (field or method) of every superclass.  A class whose declaration omits the extends keyword, nevertheless implicitly extends the Object class.  Therefore, Object is the master superclass at the top of every inheritance chain and every Java class inherits its members.  Multiple subclasses may extend the same superclass, but not vice-versa (there is no multiple inheritance).  An inheritance hierarchy is a pyramid structure of subclasses extending downward from Object, and it includes one inheritance chain for each bottom node.

When an object is constructed, each of the fields declared in its class and chain of superclasses is allocated a distinct memory location, regardless of whether the same field name has been declared by multiple classes.  However, a member name declared in a given class hides the like-named member declared in the closest superclass.  The visibility of this distinct member extends downward through all subclasses until the next one that hides it by declaring the same name.  To expose the closest hidden member, a class can append the prefix, super to the name.

Another, inconsistent purpose of super is to provide shorthand notation for the constructor of a superclass.  The subclass constructor, to call one of its superclass’ constructors, can include as its first statement: super(params), where (params) matches the signature of the superclass constructor.  Without this statement, the subclass constructor will call the default superclass’ no-arg constuctor, by default.

The following examples demonstrate an inheritance chain of three classes:

Example 1 is class, TestChainBase, the base class.

Example 2 is class, TestChainMid, the direct subclass of the base class.

Example 3 is class, TestChainLow, the lowest level subclass.

Example 4 is class, TestChain, which demonstrates how data hidden by overloaded names can be exposed.  Note how the OOP property of Polymorphism assures that an object knows which overridden method to invoke regardless of the type of the reference variable pointing to it.


Example 1

The base class.

public class TestChainBase {
   private int testPriv = 11;      // instance variables
   private int basPriv  = 12;      // with default values
   public  int testPub  = 13;
   public  int basPub   = 14;

   public int getTestPriv() {      // overridden by subclasses
      System.out.println(" getTestPriv method of class TestChainBase");
      return testPriv;
   }
   public int getBasePriv() {      // class-specific get method
      return basPriv;
   }
   public String toString() {      // class-specific toString
      return "Base: testPriv = " + testPriv +
                 ", basPriv = "  + basPriv  +
                 ", testPub = "  + testPub  +
                 ", basPub = "   + basPub;
   }   
}

Example 2

The middle derived class.

public class TestChainMid extends TestChainBase {
   private int testPriv;             // instance variables
   private int midPriv;
                                     // this class omits testPub
   public  int midPub;

   public TestChainMid(int tpriv, int mpriv, int mpub) {
      testPriv = tpriv;              // implied call to superclass' 
      midPriv  = mpriv;              // default no-arg constructor
      midPub   = mpub;
   }
   public int getTestPriv() {        // overridden by subclasses
      System.out.println(" getTestPriv method of class TestChainMid");
      return testPriv;
   }
   public int getTestPrivUp1() {     // return hidden testPriv
      return super.getTestPriv();    // one level higher
   }
   public int getMidPriv() {         // class-specific get method
      return midPriv;
   }
   public String toString() {        // class-specific toString
      return super.toString() + "\n" // variables defined in superclasses
           + "Mid:  testPriv = " + testPriv +
                 ", midPriv = "  + midPriv  +
                 ",           "  + "  "     +   // testPub omitted
                 "  midPub = "   + midPub;
   }   
}

Example 3

The lowest-level derived class.

public class TestChainLow extends TestChainMid {
   private int testPriv;             // instance variables
   private int lowPriv;
   public  int testPub;              // hides TestChainBase version
   public  int lowPub;

   public TestChainLow(int tpriv, int lpriv, int tpub, int lpub) {
      super(21, 22, 24);             // call superclass constructor
      testPriv = tpriv;
      lowPriv  = lpriv;
      testPub  = tpub;
      lowPub   = lpub;
   }
   public int getTestPriv() {        // overrides superclass methods
      System.out.println(" getTestPriv method of class TestChainLow");
      return testPriv;
   }
   public int getTestPrivUp1() {     // return hidden testPriv
      return super.getTestPriv();    // one level higher
   }
   public int getTestPrivUp2() {     // return hidden testPriv
      return super.getTestPrivUp1(); // two levels higher
   }
   public int getTestPubUp1() {      // return hidden testPub
      return super.testPub;          // one level higher - skip TestChainMid
   }
   public int getLowPriv() {         // class-specific get method
      return lowPriv;
   }
   public String toString() {        // class-specific toString
      return super.toString() + "\n" // variables defined in superclasses
           + "Low:  testPriv = " + testPriv +
                 ", lowPriv = "  + lowPriv  +
                 ", testPub = "  + testPub  +
                 ", lowPub = "   + lowPub;
   }   
}

Example 4

A program that displays all of the object’s data members.

public class TestChain {
   public static void main(String[] args) {
      TestChainLow  low = new TestChainLow(31, 32, 33, 34);  // create new obj
      TestChainMid  mid = low;                    // define subclass references
      TestChainBase bas = low;                    // for later use

      System.out.println("\nThe entire object:");       // display the object
      System.out.println(low.toString() + "\n");

                              // display private variables with unique names
      System.out.println("The unique private variables" +
                         " - calling class-specific get methods:");
      System.out.println("basPriv = " + low.getBasePriv()
                     + ", midPriv = " + low.getMidPriv()
                     + ", lowPriv = " + low.getLowPriv() + "\n");

                              // display public variables with unique names
      System.out.println("The unique public variables:");
      System.out.println("basPub = " + low.basPub
                     + ", midPub = " + low.midPub
                     + ", lowPub = " + low.lowPub + "\n");

                              // display all versions of testPriv by calling
                              // super versions of same-named get methods
      System.out.println("The same-named private variables" +
                         " - calling super of same-named get methods:");
      System.out.println("Base testPriv = " + low.getTestPrivUp2()
                      + ", Mid testPriv = " + low.getTestPrivUp1()
                      + ", Low testPriv = " + low.getTestPriv() + "\n");

  // references and casts to superclasses just access the lowest-level method
      System.out.println("The same-named private variables - "
                       + "This does NOT work!\n"
                       + "The following always call the lowest-level"
                       + " version of getTestPriv()\n"
                       + "1) using subclass references:");
      System.out.println("Base testPriv = " + bas.getTestPriv()
                     + ", Mid testPriv = "  + mid.getTestPriv()
                     + ", Low testPriv = "  + low.getTestPriv() + "\n");
      System.out.println("2) using casts:");
      System.out.println("Base testPriv = " + ((TestChainBase)low).getTestPriv()
                      + ", Mid testPriv = " + ((TestChainMid)low).getTestPriv()
                      + ", Low testPriv = " + low.getTestPriv() + "\n");

                               // display all versions of testPub one-by-one
      System.out.println("The same-named public variables - This works!\n"
                       + "1) using subclass references:");
      System.out.println("Base testPub = " + bas.testPub
                      + ", Low testPub = " + low.testPub + "\n");
      System.out.println("2) using casts:");
      System.out.println("Base testPub = " + ((TestChainBase)low).testPub
                      + ", Low testPub = " + low.testPub + "\n");
      System.out.println("3) calling methods that return super values:");
      System.out.println("Base testPub = " + low.getTestPubUp1()
                      + ", Low testPub = " + low.testPub + "\n");
   }
}

Copyright © 2003 The Stevens Computing Services Company, Inc.  All rights reserved.