The Object Reference super – Object-Oriented Programming

The Object Reference super – Object-Oriented Programming

5.2 The Object Reference super

The this reference can be used in non-static code to refer to the current object (§3.5, p. 106). The keyword super, in contrast, can be used in non-static code to access fields and invoke methods from the superclass. The keyword super provides a reference to the current object as an instance of its superclass. In method invocations with super, the method from the superclass is invoked regardless of what the actual type of the current object is or whether the current class overrides the method. This approach is typically used to invoke methods that are overridden and to access members that are hidden to the subclass. Unlike the this keyword, the super keyword cannot be used as an ordinary reference. For example, it cannot be assigned to other references or cast to other reference types.

Example 5.4 uses the superclass Light and its subclass TubeLight from Example 5.2, which are also shown in Figure 5.2. In Example 5.4, the class NeonLight extends the class TubeLight. The declaration of the method demonstrate() at (11) in the class NeonLight makes use of the super keyword to access members higher in its inheritance hierarchy. This is the case when the showSign() method is invoked at (12). This method is defined at (4) in the class Light, rather than in the direct superclass TubeLight of the subclass NeonLight. The overridden method energyCost() at (7) and its overloaded version at (8) in the class TubeLight are invoked, using the object reference super at (13) and (14), respectively.

The superclass Light has a field named lightType and a method named energyCost defined at (1) and (2), respectively. One might be tempted to use the syntax super.super.energyCost(20) in the subclass NeonLight to invoke this method, but this is not a valid construct. One might also be tempted to cast the this reference to the class Light and try again, as shown at (15). The output shows that the method energyCost() at (7) in the class TubeLight was executed, not the one from the class Light. The reason is that a cast simply changes the type of the reference (in this case to Light), not the class of the object (which is still NeonLight). Method invocation is determined by the class of the current object, resulting in the inherited method energyCost() in the class TubeLight being executed. There is no way to invoke the method energyCost() in the class Light from the subclass NeonLight, without declaring a reference of the type Light.

At (16), the keyword super is used to access the field lightType at (6) in the class TubeLight, but the keyword super is redundant in this case. At (17), the field light-Type from the class Light is accessed successfully by casting the this reference, because it is the type of the reference that determines which field is accessed. From non-static code in a subclass, it is possible to directly access fields in a class higher in the inheritance hierarchy by casting the this reference. However, it is futile to cast the this reference to invoke instance methods in a class higher in the inheritance hierarchy, as illustrated earlier for the overridden method energyCost().

Finally, the calls to the static methods at (18) and (19) using the super and this references, respectively, exhibit runtime behavior analogous to accessing fields, as discussed previously.

Example 5.4 Using the super Keyword

Click here to view code image

// File: Client3.java
//Exceptions
class InvalidHoursException extends Exception {}
class NegativeHoursException extends InvalidHoursException {}
class ZeroHoursException extends InvalidHoursException {}
class Light {
  protected String lightType = “Generic Light”;   // (1) Instance field
  protected double energyCost(int noOfHours)      // (2) Instance method
      throws InvalidHoursException {
    System.out.print(“>> Light.energyCost(int): “);
    if (noOfHours < 0)
      throw new NegativeHoursException();
    double cost = 00.20 * noOfHours;
    System.out.println(“Energy cost for ” + lightType + “: ” + cost);
    return cost;
  }
  public Light makeInstance() {                   // (3) Instance method
    System.out.print(“>> Light.makeInstance(): “);
    return new Light();
  }
  public void showSign() {                        // (4) Instance method
    System.out.print(“>> Light.showSign(): “);
    System.out.println(“Let there be light!”);
  }
  public static void printLightType() {           // (5) Static method
    System.out.print(“>> Static Light.printLightType(): “);
    System.out.println(“Generic Light”);
  }
}
//______________________________________________________________________________
class TubeLight extends Light {
  public static String lightType = “Tube Light”;  // (6) Hiding field at (1).
  @Override
  public double energyCost(final int noOfHours)   // (7) Overriding instance
      throws ZeroHoursException {                 //     method at (2).
    System.out.print(“>> TubeLight.energyCost(int): “);
    if (noOfHours == 0)
      throw new ZeroHoursException();
    double cost = 00.10 * noOfHours;
    System.out.println(“Energy cost for ” + lightType + “: ” + cost);
    return cost;
  }
  public double energyCost() {          // (8) Overloading method at (7).
    System.out.print(“>> TubeLight.energyCost(): “);
    double flatrate = 20.00;
    System.out.println(“Energy cost for ” + lightType + “: ” + flatrate);
    return flatrate;
  }
  @Override
  public TubeLight makeInstance() {     // (9) Overriding instance method at (3).
    System.out.print(“>> TubeLight.makeInstance(): “);
    return new TubeLight();
  }
  public static void printLightType() { // (10) Hiding static method at (5).
    System.out.print(“>> Static TubeLight.printLightType(): “);
    System.out.println(lightType);
  }
}
//______________________________________________________________________________
class NeonLight extends TubeLight {
  // …
  public void demonstrate()                       // (11)
      throws InvalidHoursException {
    super.showSign();                             // (12) Invokes method at (4)
    super.energyCost(50);                         // (13) Invokes method at (7)
    super.energyCost();                           // (14) Invokes method at (8)
    ((Light) this).energyCost(50);                // (15) Invokes method at (7)
    System.out.println(super.lightType);          // (16) Accesses field at (6)
    System.out.println(((Light) this).lightType); // (17) Accesses field at (1)
    super.printLightType();                       // (18) Invokes method at (10)
    ((Light) this).printLightType();              // (19) Invokes method at (5)
  }
}
//______________________________________________________________________________
public class Client3 {
  public static void main(String[] args)
      throws InvalidHoursException {
    NeonLight neonRef = new NeonLight();
    neonRef.demonstrate();
  }
}

Output from the program:

Click here to view code image

>> Light.showSign(): Let there be light!
>> TubeLight.energyCost(int): Energy cost for Tube Light: 5.0
>> TubeLight.energyCost(): Energy cost for Tube Light: 20.0
>> TubeLight.energyCost(int): Energy cost for Tube Light: 5.0
Tube Light
Generic Light
>> Static TubeLight.printLightType(): Tube Light
>> Static Light.printLightType(): Generic Light