Inheriting Members in Java

© Copyright University of New Haven 1998

Back to Home Page

When class Q extends P , then class Q becomes a special "kind of" class P and class Q is said to "inherit" class P's members (unless those members are made invisible, for example by a private qualifier).

Since class Q has (i.e. inherits) all the (visible) members of class P , and may perhaps have additional members, it could presumably be used wherever a class P could be used. The view taken by Java, among other object-oriented languages, is that classes with fewer members are more general than their descendants with more members. As a class "extends" by addition (or over-riding) of members, it becomes more special. The more special class Q can appear wherever the more general class P appears because presumably a Q instance can do anything that a P instance could do, and maybe even more with its (Q 's) additional members. Thus:

     Q qq= new Q();
     P pp= qq;        // valid when Q is a descendant of P
is permissible. Here both qq , of type Q , and pp, of type P , refer to the same Q instance. In contrast:
     Q cc= new P();   // INVALID when Q is a descendant of P
is not permissible, for Q cc is a special "kind of " class P and therefore appears to be able to refer to members of its class Q . Unfortunately, these members may be absent from the more general class P instance to which it would refer. This might lead to a reference to a missing or invalid member, and is thus impermissible. Thus, a more general ancestor class (higher in the inheritance hierarchy) can point to a more special descendant class (lower in the inheritance hierarchy), but a more special ("lower") class can not point to a more general ("higher") class.

When inheritance occurs, the descendant class (e.g. class Q) can declare members with names and declarations identical to those inherited from its ancestor(s), which declarations are said to "override" the inherited declarations. Of course, the descendant class can declare additional names too. For example:

     class P
          int x= 1;
          int y= 3;
          void method() { System.out.println("P"); }

     class Q extends P
          int x= 2;
          int z= 4;
          void method() { System.out.println("Q"); }
Here, class P's members, x and method, are overridden in class Q, while member y is inherited and member z is additional.

A Java class inherits data members (e.g. x and y) differently from methods (e.g. method). Methods use virtual inheritance while data members use non-virtual inheritance. This means that a reference to a method depends on the instance, but a reference to a data member depends on the reference type. This shows up when a member is over-ridden. A reference to a (virtual) method always selects the over-riding method, but a reference to a data member always selects the member associated with the reference type, which may be either the over-riding member, or one of many over-ridden members in the inheritance hierarchy. An example:

    System.out.println(qq.x);  // Output: 2
    qq.method();               // Output: Q
    System.out.println(pp.x);  // Output: 1
    pp.method();               // Output: Q
Since both qq and pp refer to the same instance, they also refer to the same method... Q's overriding member method. However reference to data members is different. Since qq's type is Q , it refers to Q's data member x with value 2. But since pp's type is P , it refers to P's data member x with value 1 which is the x inherited from P, even though it appears to be overridden in class Q. This is so, even though pp refers to an instance of class Q , because a data member reference depends on the reference type (i.e. P), not the instance type.

The remaining data members, y and z, behave as expected:

    System.out.println(pp.y);  // Output: 3
    System.out.println(qq.y);  // Output: 3
//  System.out.println(pp.z);  INVALID
    System.out.println(qq.z);  // Output: 4
Note that the member reference pp.z is invalid because pp is of type P which has no member z, despite the fact that pp happens to refer to an instance of type Q, which instance does have a member z. Thus member z can only be used with a reference of type Q (or a descendant of Q).... the more general reference (ancestor P ) can NOT refer to a more special instance (descendant Q ).

Home Page