Implementing Inheritance

Objective

After completing this lesson, you will be able to Implement a specialized class.

Inheritance Implementation

Watch this video to understand the concept of general and more specialized classes.

Inheritance - The "Is A" Relationship

In modeling, we talk about specialization. The implementation of this relationship in a programming language uses the concept of inheritance, in which a new class (the subclass) is derived from an existing one (the superclass). A reliable test to see if there is a genuine inheritance relationship between two classes is to try to say "a subclass is a superclass". If this is clearly true, you have an inheritance relationship. If it is clearly not true, or even sounds a bit strange, you do not have an inheritance relationship and should not implement one as it will likely cause problems further down the line. When testing our model, we can definitely say that "a cargo plane is a plane " and "a passenger plane is a plane".

A class that inherits from a superclass automatically contains all of the components of the superclass. So, for example, the passenger plane class has a manufacturer and a type just as a plane does. This should not be surprising, as a passenger plane is a plane. What may at first appear suprising is that the passenger plane class cannot access its own manufacturer and type. This is because they are private attributes of the superclass and, as such, they are not accessible to the subclass. Later on you will learn a way of allowing a subclass to access components of a superclass without making them fully public.

Defining the Inheritance Relationship

In ABAP, you implement inheritance using the INHERITING FROM addition in the class definition statement. In this example, the passenger plane class is defined as a subclass of the plane class. Note that the subclass knows its superclass, but the airplane class has no idea what subclasses it has (if any).

A subclass may only have one direct superclass.

One Superclass - Any Number of Subclasses

The plane class may have any number of subclasses, but it does not know about any of them.

Each subclass has a single direct superclass. It knows its superclass, but does not know about any sibling classes (other classes that are derived from the same superclass). In our example, the passenger plane class has no knowledge of the cargo plane class.

How to Extend a Subclass

Once you have declared a subclass, you need to add new components to it or change the existing components. This is what turns the mere copy of the plane class into a passenger plane or a cargo plane. Thanks to inheritance, you do not need to implement the class from scratch - you merely extend it by the new components that it needs.

There are three ways to do this:

Add new components: You can declare new attributes, types, constants, and methods in the class. Their names must not clash with other names that have already been used to declare components in the superclass. You must also ensure that the new components are only relevant to the subclass, and not to all specializations of the superclass. If you declare a new attribute or method that is generally relevant, you must move it into the superclass.

Redefine methods: When you call an inherited method, the runtime system executes the implementation of the method from the superclass. This implementation cannot, however, take into account any new components that you have declared in the subclass. In this case, you may redefine the method. This means that you can assign it a new implementation that is relevant to the subclass.

Add a new constructor: Constructors ensure that new instances of the class are properly initalized. When you create an instance of a subclass, the runtime system always executes the constructor of the superclass. This ensures, for example, that a passenger plane has a manufacturer and a type in just the same way as any other plane. However, the constructor of the superclass cannot initialize attributes of the subclass, as it does not know that they exist. For this reason, you can define a new constructor for the subclass.

Declaring New Components

You can extend a subclass by declaring new components. These are visible to the class itself but not to the superclass, and also not to any other classes that inherit from the same superclass. You therefore need to consider whether the new components are only relevant in the current class or if they actually need to be declared in the superclass.

Protected Components

Even though a subclass contains all of the attributes of its superclass, it is not allowed to access the private components directly by itself. Sometimes this is necessary, but there are other times when a superclass needs to let its subclasses access particular attributes or methods without making them fully public. It can do so by declaring them in the protected section. Protected components are visible within the class itself but also to all subclasses.

In the class definition, you introduce the protected section using the statement PROTECTED SECTION. You must always declare the visibility sections in the sequence PUBLIC SECTION - PROTECTED SECTION - PRIVATE SECTION.

You can move an existing component of a class using a quick fix. To do so, place the cursor on the name of the component in ADT and press Ctrl + 1, then choose the quick fix make <element> protected.

Moving a private component to the protected section is a compatible change - you have broadened the visibility of the component. However, making a public component protected is an incompatible change and could cause syntax errors. This is because you have restricted the visibility of the component, which might already have been used outside the inheritance hierarchy.

Redefining Methods

Superclasses often contain methods that you want to use in a subclass, but their implementations are unsuitable as they do not take into account the particular nature of the subclass. For example, the get_attributes method of the plane class cannot return attributes that are defined in the passenger plane class. However, the passenger plane class still needs a method get_attributes.

In ABAP, you can solve this problem by redefining the instance method get_attributes. When you redefine a method, you write a new implementation for it in the subclass that takes into account things that only the subclass can know. However, the definition of the method stays exactly the same. You must follow these rules:

  • The method retains the same name
  • You indicate that you want to redefine the method by declaring it in the subclass with the addition REDEFINITION.
  • The method has the same visibility as in the superclass
  • You must not change the signature of the method

You cannot redefine a static method.

Implementing the Redefinition

When you implement the redefinition of a method, you can call the implementation in the superclass. You do this using the implicit object reference super, which points to the superclass within the current instance. In this way, you can re-use the method implementation from the superclass and then add to it in your own implementation.

You do not need to declare the reference variable super. It is automatically available within the implementation of redefined methods.

Calling the method in the superclass in this way is optional. Sometimes it will be a good idea, sometimes there is nothing to be gained by it.

New Constructor Definition

In a subclass, you may define a new constructor. In contrast to method redefinitions, a constructor may have its own signature. The signature of a constructor will often contain the same parameters as the constructor of the superclass, plus new parameters of its own. This is because the new instance constructor has two tasks - firstly to call the constructor of the superclass, and then to take care of initalizing its own attributes that are specific to the subclass.

As well as defining a new constructor, you can also define a new static constructor in a subclass.

Role of the Constructor

The constructor of a subclass has two functions; it must ensure that the instance of the superclass is properly created, and after that, it can set the initial values of its own attributes.

In our example, since a passenger plane is a plane, there can be no passenger plane instance unless the constructor of the plane class has run successfully to set the manufacturer and type of the new passenger plane. Only after this can the passenger plane class set its own attributes.

The constructor of the subclass must therefore be able to set not only its own attributes, but to pass the required values to the constructor of the superclass. This is why the signature of the constructor in the subclass often contains the entire signature of the constructor that it is required to call.

Sequence of New Constructor Implementation

In the implementation of the new constructor, there is a particular sequence of operations that you must observe. The most important event is the call to the constructor of the superclass. This is obligatory and you call the method using the implicit reference super, just as you can when calling the original implementation of a redefined method. Once again, you do not need to explicitly declare super.

Before you have called the constructor of the superclass, you must not address any instance components of the instance that is under construction. You may address static components of the class and check the correctness of the importing parameters of the constructor. If they are not correct, raise an exception.

Once the constructor of the superclass has run successfully, you may address the instance components of your new instance.

Sequence of Constructor Calls

The figure illustrates the sequence in which static and instance constructors are called. The example assumes that a program wants to instantiate the passenger plane class and that the plane class has not previously been addressed.

When the program tries to create an instance of the passenger plane class, the runtime system automatically calls the static constructors sequentially, starting at the top of the inheritance hierarchy. Once all of the static constructors have run, the runtime system calls the instance constructor of the subclass, which in turn calls the instance constructor of the superclass.

Try It Out: Inheritance Implementation

  1. Create a new class that implements the interface IF_OO_ADT_CLASSRUN.
  2. Switch to the Local Types tab and copy the following code snippet into the editor:
    Code Snippet
    Copy code
    Switch to dark mode
    123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
    CLASS lcl_plane DEFINITION. PUBLIC SECTION. TYPES: BEGIN OF ts_attributes, name TYPE string, value TYPE string, END OF ts_attributes, * declare table - do not allow the same attribute to be used more than once tt_attributes TYPE SORTED TABLE OF ts_attributes WITH UNIQUE KEY name. METHODS constructor IMPORTING iv_manufacturer TYPE string iv_type TYPE string. METHODS: get_Attributes RETURNING VALUE(rt_Attributes) TYPE tt_attributes. PROTECTED SECTION. DATA manufacturer TYPE string. DATA type TYPE string. PRIVATE SECTION. ENDCLASS. CLASS lcl_plane IMPLEMENTATION. METHOD constructor. manufacturer = iv_manufacturer. type = iv_type. ENDMETHOD. METHOD get_attributes. rt_attributes = VALUE #( ( name = 'MANUFACTURER' value = manufacturer ) ( name = 'TYPE' value = type ) ) . ENDMETHOD. ENDCLASS. CLASS lcl_cargo_plane DEFINITION INHERITING FROM lcl_plane. PUBLIC SECTION. METHODS constructor IMPORTING iv_manufacturer TYPE string iv_type TYPE string iv_cargo TYPE i. METHODS get_attributes REDEFINITION. private section. data cargo type i. ENDCLASS. CLASS lcl_cargo_plane IMPLEMENTATION. METHOD constructor. super->constructor( iv_manufacturer = iv_manufacturer iv_type = iv_type ). cargo = iv_cargo. ENDMETHOD. METHOD get_attributes. * method uses protected attributes of superclass rt_attributes = value #( ( name = 'MANUFACTURER' value = manufacturer ) ( name = 'TYPE' value = type ) ( name ='CARGO' value = cargo ) ). ENDMETHOD. ENDCLASS. CLASS lcl_passenger_plane DEFINITION INHERITING FROM lcl_Plane. PUBLIC SECTION. METHODS constructor IMPORTING iv_manufacturer TYPE string iv_type TYPE string iv_seats TYPE i. METHODS get_Attributes REDEFINITION. private section. data seats type i. ENDCLASS. CLASS lcl_passenger_plane IMPLEMENTATION. METHOD constructor. super->constructor( iv_manufacturer = iv_manufacturer iv_type = iv_type ). ENDMETHOD. METHOD get_attributes. * Redefinition uses call of superclass implementation rt_attributes = super->get_attributes( ). rt_Attributes = value #( base rt_attributes ( name = 'SEATS' value = seats ) ). ENDMETHOD. ENDCLASS.
  3. Activate the class by pressing Ctrl + F3.
  4. Familiarize yourself with the source code by using editor features like F2 documentation and the navigation functions.

    Note

    There is no executable code in this example.

Using Abstract and Final Components

You can declare a class as abstract. This means that a program cannot instantiate it. In our example with airplanes, passenger planes, and cargo planes, you might decide that , in the real world, there are only passenger planes and cargo planes. However, from the point of view of software modeling, it is useful to have the class lcl_plane to hold the components that cargo planes and passenger planes have in common. By using the abstract class, you can implement all of the common components in the plane class without having to worry that somebody might create instances of the class by mistake.

If a class is abstract, you can still access its static components. These are:

  • Static methods
  • Static attributes
  • Constants
  • Type definitions

Although you can implement the methods of an abstract class, you can also declare an individual method as abstract. In this case, there is no implementation for it in the class in which it is declared. Note that you can only declare an abstract method in an abstract class.

Subclasses of the abstract class can only be instantiated once the abstract method has been redefined.

You can declare a class as FINAL. This means that it cannot have any subclasses.

In a class that is not itself finaly, you can declare methods as final. This means that, although the class itself may have subclasses, the method itself cannot be redefined. You might do this if a method contains an authorization check. Otherwise, a subclass might redefine the method and leave out the authorization check.

Implement Inheritance

You notice that your local classes LCL_PASSENGER_FLIGHT and LCL_CARGO_FLIGHT have a lot in common and contain redundant implementations. You realize that from a model perspective, you can perceive passenger flights and cargo flights as two specializations of flights in general. Therefore, you introduce a common superclass LCL_FLIGHT with the common definitions and implementations.

Template:

  • /LRN/CL_S4D401_ACS_AUTH_CHECK (Global Class)

Solution:

  • /LRN/CL_S4D401_OOS_INHERITANCE (Global Class)

Task 1: Copy Template (Optional)

Copy the template class. If you finished the previous exercise, you can skip this task and continue editing your class ZCL_##_SOLUTION.

Steps

  1. Copy class /LRN/CL_S4D401_ACS_AUTH_CHECK to a class in your own package (suggested name: ZCL_##_SOLUTION, where ## stands for your group number).

    1. In the Project Explorer view, right-click class /LRN/CL_S4D401_ACS_AUTH_CHECK to open the context menu.

    2. From the context menu, choose Duplicate ....

    3. Enter the name of your package in the Package field. In the Name field, enter the name ZCL_##_SOLUTION, where ## stands for your group number.

    4. Adjust the description and choose Next.

    5. Confirm the transport request and choose Finish.

  2. Activate the copy.

    1. Press Ctrl + F3 to activate the class.

Task 2: Define the Superclass

Define a new local class LCL_FLIGHT and let local classes LCL_PASSENGER_FLIGHT and LCL_CARGO_FLIGHT inherit from it. Compare the two subclasses and move those attributes and method definitions that are identical in both classes to the superclass.

Steps

  1. In your global class ZCL_##_SOLUTION, use a code template to define a new local class LCL_FLIGHT.

    Note

    Make sure you define the new class at the beginning of source code on the Local Types tab so that you can reference it in the existing local classes.
    1. In your global class ZCL_##_SOLUTION, switch to the Local Types tab.

    2. At the beginning of the source code, type lcl and press Ctrl + Space to invoke code completion.

    3. From the list of suggested completions, choose lcl - Local class.

    4. While lcl_ is still highlighted, enter lcl_flight to adjust the class name in the definition and the implementation part.

    5. The code template should have added the following code:

      Code Snippet
      Copy code
      Switch to dark mode
      12345678910111213
      CLASS lcl_flight DEFINITION CREATE PRIVATE. PUBLIC SECTION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS lcl_flight IMPLEMENTATION. ENDCLASS.
  2. Make sure that it is not possible to create instances of LCL_FLIGHT.

    1. Remove the CREATE PRIVATE addition and replace it with an ABSTRACT addition.

    2. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      12
      *CLASS lcl_flight DEFINITION CREATE PRIVATE.
  3. Let local class LCL_PASSENGER_FLIGHT inherit from local class LCL_FLIGHT.

    1. Scroll down to the definition part of local class LCL_PASSENGER_FLIGHT.

    2. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123
      CLASS lcl_passenger_flight DEFINITION .
  4. Fix the syntax errors In the constructor method, instance attributes … can be accessed only after the constructor of the superclass … is called. by adding super->constructor( ). at the beginning of the implementation of method CONSTRUCTOR in subclass LCL_PASSENGER_FLIGHT.

    Note

    We will have a more detailed look at the constructor logic later in this exercise.
    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      12
      METHOD constructor.
  5. Use a quick fix to move ("pull") the following public components from subclass LCL_PASSENGER_FLIGHT to the superclass LCL_FLIGHT:

    • Attribute carrier_id
    • Attribute connection_id
    • Attribute flight_date
    • Structure type ST_CONNECTION_DETAILS
    • Method GET_CONNECTION_DETAILS
    1. In the definition part of local class LCL_PASSENGER_FLIGHT, place the cursor on carrier_id and choose Ctrl + F1 to display a list of suggested quick fixes.

    2. From the list of available quick fix, choose Pull-up carrier_id to superclass lcl_flight

    3. Repeat for the other components listed above.

    4. The definition part of superclass LCL_FLIGHT should now look like this:

      Code Snippet
      Copy code
      Switch to dark mode
      12345678910
      CLASS lcl_flight DEFINITION ABSTRACT. PUBLIC SECTION. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS.
  6. Use a quick fix to move ("pull") the following private components from subclass LCL_PASSENGER_FLIGHT to the superclass LCL_FLIGHT:

    • Attribute planetype
    • Attribute connection_details
    1. In the definition part of local class LCL_PASSENGER_FLIGHT, place the cursor on planetype and choose Ctrl + F1 to display a list of suggested quick fixes.

    2. From the list of available quick fix, choose Pull-up planetype to superclass lcl_flight

    3. Repeat for attribute connection_details.

    4. The definition part of superclass LCL_FLIGHT should now look like this:

      Code Snippet
      Copy code
      Switch to dark mode
      123456789101112131415161718192021222324252627
      CLASS lcl_flight DEFINITION ABSTRACT. PUBLIC SECTION. TYPES: BEGIN OF st_connection_details, airport_from_id TYPE /dmo/airport_from_id, airport_to_id TYPE /dmo/airport_to_id, departure_time TYPE /dmo/flight_departure_time, arrival_time TYPE /dmo/flight_departure_time, duration TYPE i, END OF st_connection_details. DATA carrier_id TYPE /dmo/carrier_id READ-ONLY. DATA connection_id TYPE /dmo/connection_id READ-ONLY. DATA flight_date TYPE /dmo/flight_date READ-ONLY. METHODS: get_connection_details RETURNING VALUE(r_result) TYPE st_connection_details. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS.
  7. Let local class LCL_CARGO_FLIGHT inherit from local class LCL_FLIGHT.

    1. Scroll down to the definition part of local class LCL_CARGO_FLIGHT.

    2. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123
      CLASS lcl_cargo_flight DEFINITION .
  8. Fix the syntax errors In the constructor method, instance attributes … can be accessed only after the constructor of the superclass … is called. as before.

    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      12
      METHOD constructor.
  9. Comment or remove the redundant definitions from local class LCL_CARGO_FLIGHT.

    Hint

    Use the syntax errors in the Problems view to navigate to the redundant definitions.
    1. In the Problems view, double-click syntax error There is already a type calls "ST_CONNECTION_DETAILS".

    2. Remove or comment the definition of structure type ST_CONNECTION_DETAILS in local class LCL_CARGO_FLIGHT.

    3. Repeat for the other components that are already defined in superclass LCL_FLIGHT.

      Note

      Do not forget to also remove or comment the implementation of method GET_CONNECTION_DETAILS.

Task 3: Redefine a Method

Define a GET_DESCRIPTION method in the superclass and redefine it in the subclasses. Move the common parts of the implementation to the superclass and keep the specific part in the subclasses.

Steps

  1. In the superclass LCL_FLIGHT, define a public instance method GET_DESCRIPTION with the same parameters as in the subclasses.

    Hint

    Copy the method definition from one of the subclasses.
    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      12345
      CLASS lcl_flight DEFINITION ABSTRACT. PUBLIC SECTION.
  2. In local classes LCL_PASSENGER_FLIGHT and LCL_CARGO_FLIGHT, replace the definitions of method GET_DESCRIPTION by redefinitions.

    Hint

    Use the syntax errors in the Problems view to navigate to the redundant definitions.
    1. In the definition part of both subclasses adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123456
      * METHODS get_description * RETURNING * VALUE(r_result) TYPE string_table. METHODS get_description REDEFINITION.
  3. Use a quick fix to add an empty implementation for the new method.

    1. In the definition of local class LCL_FLIGHT, place the cursor on get_description and press Ctrl + 1 to invoke the quick fix.

    2. Alternatively, you can choose the error icon with the light bulb on the left-hand side of the code.

    3. From the suggestion list, choose Add implementation for get_description.

  4. In local class LCL_PASSENGER_FLIGHT, adjust the implementation of method GET_DESCRIPTION. At the beginning of the implementation, call the implementation from the superclass and store the result in parameter r_result.

    1. Add the following code:

      Code Snippet
      Copy code
      Switch to dark mode
      123
      r_result = super->get_description( ).
  5. Move the output of the inherited attributes carrier_id, connection_id, flight_date, and planetype to the implementation in superclass LCL_FLIGHT.

    1. Select the declaration and preparation of local variable txt and the first two APPEND statements and press Ctrl + C to copy the code snippet to the clipboard.

    2. Press Ctrl + < to add a comment sign at the beginning of the selected code rows.

    3. Switch to the implementation of method GET_DESCRIPTION in local class LCL_FLIGHT, place the cursor between statements METHOD … and ENDMETHOD and press Ctrl + V to insert the code snippet.

    4. The implementation in the superclass should then look like this:

      Code Snippet
      Copy code
      Switch to dark mode
      1234567891011121314151617
      METHOD get_description. DATA txt TYPE string. txt = 'Flight &carrid& &connid& on &date& from &from& to &to&'(005). txt = replace( val = txt sub = '&carrid&' with = carrier_id ). txt = replace( val = txt sub = '&connid&' with = connection_id ). txt = replace( val = txt sub = '&date&' with = |{ flight_date DATE = USER }| ). txt = replace( val = txt sub = '&from&' with = connection_details-airport_from_id ). txt = replace( val = txt sub = '&to&' with = connection_details-airport_to_id ). APPEND txt TO r_result. APPEND |{ 'Planetype:'(006) } { planetype } | TO r_result. ENDMETHOD.
  6. In local class LCL_CARGO_FLIGHT, adjust the implementation of method GET_DESCRIPTION accordingly. Call the inherited implementation and remove or comment the redundant APPEND statements.

    1. Adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      12345678
      METHOD get_description. APPEND |Maximum Load: { maximum_load } { load_unit }| TO r_result. APPEND |Free Capacity: { get_free_capacity( ) } { load_unit }| TO r_result. ENDMETHOD.

Task 4: Add a Common Constructor

In superclass LCL_FLIGHT, define an instance constructor with those input parameters that the two subclass constructors have in common. Adjust the call of the superclass constructor in the subclasses so that they forward the common parameters. Then move the common parts of the constructor logic from the subclass constructors to the superclass constructor.

Steps

  1. In the superclass LCL_FLIGHT, define a CONSTRUCTOR method having the same input parameters as the CONSTRUCTOR of the subclass LCL_PASSENGER_FLIGHT.

    Hint

    There is no Pull-up quick fix for instance constructor methods. Instead, copy and paste the method definition from the subclass to the superclass.
    1. Scroll down to the definition part of local class LCL_PASSENGER_FLIGHT.

    2. Select the complete METHODS statement for method CONSTRUCTOR and press Ctrl + C to copy the code snippet to the clipboard.

    3. Scroll up to the definition part of local class LCL_FLIGHT, place the cursor after the PUBLIC SECTION statement and press Ctrl + V to insert the code snippet from the clipboard.

  2. Adjust the call of the superclass constructor in the subclasses. Forward the importing parameters to the super constructor.

    Hint

    Use the syntax errors in the Problems view to navigate to the calls of the superclass constructor.
    1. In both subclass constructors, adjust the code as follows:

      Code Snippet
      Copy code
      Switch to dark mode
      123456
      METHOD constructor. super->constructor( ).
  3. Use a quick fix to add an empty implementation for the superclass constructor.

    1. In the definition of local class LCL_FLIGHT, place the cursor on constructor and press Ctrl + 1 to invoke the quick fix.

    2. From the suggestion list, choose Add implementation for constructor.

  4. Move the value assignments for inherited attributes carrier_id, connection_id, and flight_date from the subclass constructors to the superclass constructor.

    1. Navigate to the implementation of method CONSTRUCTOR in local class LCL_PASSENGER_FLIGHT.

    1. Select the value assignments for inherited attributes carrier_id, connection_id, and flight_date and press Ctrl + C to copy the code snippet to the clipboard.

    2. Press Ctrl + < to add a comment sign at the beginning of the selected code rows.

    3. Switch to the implementation of method CONSTRUCTOR in local class LCL_FLIGHT, place the cursor between statements METHOD … and ENDMETHOD and press Ctrl + V to insert the code snippet.

    4. The implementation in the superclass should then look like this:

      Code Snippet
      Copy code
      Switch to dark mode
      1234567
      METHOD constructor. me->carrier_id = i_carrier_id. me->connection_id = i_connection_id. me->flight_date = i_flight_date. ENDMETHOD.
    5. Switch to the implementation of method CONSTRUCTOR in local class LCL_CARGO_FLIGHT and remove or comment the redundant value assignments.

  5. Activate and test your global class as console app. Debug the instantiation of local class LCL_PASSENGER_FLIGHT and the call of method GET_DESCRIPTION.

    1. Press Ctrl + F3.

    2. Press F9.

Log in to track your progress & complete quizzes