Try OpenEdge Now
skip to main content
Object-oriented Programming
Designing Objects: Inheritance, Polymorphism, and Delegation : Class hierarchies and inheritance : Overriding class events within a class hierarchy
 

Overriding class events within a class hierarchy

It is invalid to have an implemented class event in a subclass with the same name as any PUBLIC or PROTECTED class event in one of its super classes, regardless of whether the signatures of the two events match. Thus, it is impossible to define an event in a subclass that might override (shadow) or conflict with an implemented event in a super class without returning an error from the compiler.
Note that you must override and implement any abstract events in a non-abstract subclass that inherits them. However, if the inheriting subclass is abstract, you can, at your option, override any inherited abstract event and designate it again as abstract. The only difference you can make in creating a new abstract event definition is to specify a less restrictive access mode, overriding a protected abstract event to make it public. Note, again, like an implemented property, that you cannot override an implemented event and make it abstract.
Note also that for abstract events, unlike for abstract properties or methods, the point at which you provide the non-abstract definition of the event determines where you can publish the event. However, you can subscribe a handler for an abstract event at all the same points in a class hierarchy where you can access an abstract property or method. Thus, you can subscribe a handler for any accessible event, whether the event is abstract or implemented at the point in a class hierarchy where you create the event handler subscription. However, you can publish an implemented event only within the class definition that implements it, which is the class that contains a DEFINE EVENT statement for the event without the ABSTRACT option. For more information on publishing and subscribing event handlers for a class event inside and outside of a class hierarchy, see Publishing and subscribing to class events.
The following figure shows how a method override invoked on the defining super class executes within a class hierarchy, using pseudo-code to represent the behavior. The figure shows four classes forming an inheritance hierarchy with ClassA as the root class and ClassD as the most derived subclass. ClassA defines a MethodA( ), which is overridden, in turn, by ClassB, then by ClassC, which is also the most derived subclass that overrides the method.
This pseudo-code fragment defines an object reference (dotted arrow) to the ClassA data type (ObjectA), and according to the numbered arrows:
1. Instantiates ClassD, returning its object reference as ObjectD (1a) and executes MethodD( ) on ObjectD (1b). MethodD( ) then invokes MethodA( ) within the class hierarchy of the object. Because the most derived subclass in the object that overrides MethodA( ) is ClassC, ClassC’s definition of this method executes (1c).
2. Assigns the ClassA object reference, ObjectA, to reference the ClassD instance (2a) and executes MethodA( ) on ObjectA (2b), which references the object as a ClassA instance and invokes the same MethodA( ) override (ClassC’s) that was previously invoked through the ObjectD reference (1c).
Figure 7. Invoking an overridden method in a class
Now, suppose ClassE inherits from the ClassD and also overrides MethodA( ), as shown in the following figure.
Figure 8. Invoking an overridden method in a class extension
In this hierarchy, ClassE is the most derived subclass that overrides the method. So, as shown according to the numbered arrows, this pseudo-code:
1. Instantiates ClassE, returning its object reference as ObjectE (1a) and executes the inherited MethodD( ) on ObjectE (1b). MethodD( ) then invokes MethodA( ) within the class hierarchy of the object. Because the most derived subclass that overrides MethodA( ) is now ClassE, ClassE’s definition of that method executes (1c).
2. Assigns the ClassA object reference, ObjectA, to reference the ClassE instance (2a) and executes MethodA( ) on ObjectA (2b), which references the object as a ClassA instance and invokes the same MethodA( ) override (ClassE’s) that was previously invoked through the ObjectE reference (1c).
Note that, in general, you cannot invoke overrides of a method other than the override in the most derived subclass. For example, there is no way to directly access the MethodA( ) override defined in ClassB, even from within ClassB, because all direct calls to MethodA( ) anywhere inside or outside the class hierarchy execute the override in ClassE.
However, using the SUPER system reference, ABL allows any method in a subclass to invoke the behavior of a specified method in the nearest super class where it is defined. Thus, the MethodA( ) override defined in ClassE can invoke the behavior for the MethodA( ) definition that it overrides in ClassC. Similarly, MethodD( ) can use the SUPER system reference to invoke the behavior of the MethodA( ) defined in ClassC instead of invoking the override in the most derived subclass, ClassE. For more information on the SUPER system reference, see SUPERsystem reference.
If the super class's method is defined as PRIVATE, the method is not inherited by the subclass. If the subclass defines a method of the same name, it is not overriding the super class method because the super class's definition is not available to the subclass. In this case, the subclass method is entirely independent of the super class method. Because of this, it does not matter whether the methods match in their return type, access mode, or parameters. Also in this case, use of the OVERRIDE keyword on the method is not allowed.
If the super class's non-private method is defined as FINAL, no subclass can override that method. The compiler returns an error if you attempt to compile a subclass that has a method with the same name as a FINAL method in a super class, regardless if the signatures match.
The following sample class references and extends the sample subclass, acme.myObjs.CustObj (see Sample classes). This sample extension overrides the GetCustomerName( ) method to return an E-mail address with the customer name:
USING acme.myObjs.*.

CLASS acme.myObjs.NECustObj INHERITS CustObj:

  DEFINE PRIVATE TEMP-TABLE ttEmail NO-UNDO
     FIELD RecNum AS INTEGER
    FIELD Name   AS CHARACTER FORMAT "X(20)"
     FIELD Email  AS CHARACTER FORMAT "X(20)".

  CONSTRUCTOR PUBLIC NECustObj (INPUT EmailFile AS CHARACTER):
    ...
    /* Code to initialize ttEmail:                            */
    ...
  END CONSTRUCTOR.

  /* Override method to always get customer name and email */
  METHOD PUBLIC OVERRIDE CHARACTER GetCustomerName
    (INPUT piCustNum AS INTEGER):
     DEFINE VARIABLE EmailName AS CHARACTER NO-UNDO.

     EmailName = SUPER:GetCustomerName (piCustNum).
     FIND FIRST ttEmail WHERE ttEmail.Name = EmailName NO-ERROR.
     IF AVAILABLE (ttEmail) THEN
       RETURN EmailName + ";" + ttEmail.Email.
     ELSE
       RETURN EmailName.
  END METHOD.

END CLASS.
This extension adds a new temp-table to provide the E-mail address, which is initialized by the constructor for NECustObj. Note that only methods can be overridden, constructors and destructors are implicitly FINAL and cannot be overridden in a subclass.
Method overriding supports polymorphism, because it allows you to call a method on a super class object reference, and the method invoked depends on the actual class instance that overrides the super class definition of that method. Thus, you can invoke different behavior using an identical method call, depending on the subclass object referenced by the super class object reference. At compile time, you do not necessarily know the class type of the object whose method override will execute. However, the method override that executes is always the method definition in the most derived class that defines the method in the hierarchy of that class type.
From a practical programming viewpoint, then, method overriding simplifies the code to execute different behaviors that might otherwise require long CASE or nested IF statements to select among them. The new behavior is selected simply by referencing an instance of a different class that overrides the same method call. For more information on how to use polymorphism with method overriding, see Usingpolymorphism with classes.
You can also define multiple methods with the same name that you invoke with different signatures, a practice known as overloading. Method overloading provides a mechanism that you can use together with polymorphism to identify specific method calls, but you need to know at compile time the particular signature of each overloaded method you want to call in the class hierarchy of the object. For more information on method overloading, see Overloading methods and constructors.