© Rehan Zaidi 2019
R. ZaidiSAP ABAP Objectshttps://doi.org/10.1007/978-1-4842-4964-2_3

3. More on Object-Oriented ABAP

Rehan Zaidi1 
(1)
Dubai, United Arab Emirates
 

After the basic introduction to ABAP provided in Chapters 1 and 2, this chapter dives deeper into the topic. It starts by showing how inheritance applies to ABAP Objects. Then, the chapter covers the concept of casting—upcasting and downcasting—and how we can achieve polymorphism using casting.

Inheritance: Super and Subclasses

ABAP lets you derive (inherit) a new class from a given class. This is called inheritance and the inherited class is referred to as a subclass of the main class in question, which is known as the superclass . Within ABAP, you may have any number of classes derived from a given base class. For example, if we have class B inherited from class A, then class A is the direct superclass of class B. It is also possible to derive another class, class C, from class B. Then, both A and B are superclasses of C, but B is the direct superclass of C, as shown in Figure 3-1.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig1_HTML.jpg
Figure 3-1

Inheritance tree

The diagram shown in Figure 3-1 is referred to as the inheritance hierarchy or tree. These trees may contain multiple levels of class inheritance. (For simplicity’s sake, only two levels are shown along with single inheritance only.)
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig2_HTML.jpg
Figure 3-2

Inheritance tree of players

Within ABAP, you may have multiple direct subclasses for a given superclass. Conversely, you may have only one direct superclass of a given class (subclass). This is called a single inheritance.

You may view a superclass as having the common (general) characteristics of its subclasses. The level of specialization in the tree increases as we move down through each level. Likewise, as you move up the hierarchy (i.e., toward the root node), the classes tend to be more general.

For example, we may have a class called PLAYER that is the superclass of Football_player and Cricket_player. The PLAYER class has properties of both football and cricket players. However, the Cricket_player will have specific characteristics to that of a cricketer, which are not possessed by a football player, and vice versa.

Note

The predefined empty class called OBJECT is the root node of all inheritance trees.

There is a general class called OBJECT from which all inheritance trees originate. This class has no attributes or methods. The PLAYER class is actually a subclass of the OBJECT class. For simplicity’s sake, it is not shown in Figure 3-2.

Up until this point, we have looked at two types of class components—private and public. With the advent of inheritance, another type comes into play, protected. Of primary importance is the “protected” section within a superclass. The protected section contains components that are accessible from within the subclasses of the given superclass.

Within the superclass, there may be protected, private, and public components. The components of the superclass become part of the derived subclass(es). (Although the private components are included in the subclass, only the private and public components within are visible in the subclass.) Because of this, all public and protected components of classes within a given inheritance hierarchy must have unique names. For private components, you must make sure that they are uniquely named within a class.

An inherited class contains the components that are defined in ALL its superclasses (i.e., the direct superclass and the above).

Let’s illustrate this concept with an example.

Suppose we have the following inheritance tree, where two classes—B and C—are derived from a common superclass A. We can have a private attribute INT1 in classes A, B, and C. However, we cannot have a protected attribute INT1 in the superclass A and at the same time a public attribute INT1 defined in the subclass B.

The syntax for defining a subclass locally within a program is via the addition INHERITING FROM, shown as follows:
CLASS <MYSUBCLASS> DEFINITION INHERITING FROM <MYSUPERCLASS>.
...
ENDCLASS.
For example, in the following code excerpt, we have a player class called Football_player that is inherited from the generic class PLAYER.
class football_player DEFINITION INHERITING
      FROM player.
endclass.
Suppose within the PLAYER class, we have an instance constructor and a method called DISPLAY_PLAYER_DETAILS. We also have protected attributes called NAME, WEIGHT, and HEIGHT. The attributes are protected so that they may be addressed by the subclasses inherited from the PLAYER class. See the code block as follows.
class player DEFINITION.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i.
       methods display_player_details.
    protected section.
      data : name type string.
      data : height type i.
      data : weight type p decimals 2.
endclass.
We can then have a subclass called FOOTBALL_PLAYER that’s inherited from the given PLAYER class. Another attribute, called FOOTBALL_CLUB , has been defined that is specific to FOOTBALL_PLAYER. This class has its own instance constructor, which has four importing parameters (Football_Club is included in addition to the three parameters contained in the player’s constructor). The DISPLAY_PLAYER_DETAILS method is redefined in the FOOTBALL_PLAYER class. It contains no parameters, as in the player class:
class football_player DEFINITION INHERITING
      FROM player.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i
                          football_club type string.
      methods display_player_details REDEFINITION.
    private section.
      data : football_club type string.
endclass.

We will take a closer look at the instance constructors for subclasses and the redefinition of superclass methods in detail in upcoming sections.

While naming attributes within subclasses, a few restrictions apply. Let’s look at an example. Suppose you try to define a private attribute with the name HEIGHT within the FOOTBALL_PLAYER class, as shown:
class  football_player DEFINITION INHERITING
      FROM player. "BADCODE
.......
    private section.
      data  height type i.  """ NOT ALLOWED
endclass.
In this case, a syntax error occurs (see Figure 3-3).
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig3_HTML.jpg
Figure 3-3

Syntax error

This is because the HEIGHT attribute is already declared in the superclass PLAYER as a protected attribute. The protected and public attribute of the superclass becomes part of the derived subclass. If the superclass contained HEIGHT in its private section instead, this code would not have given an error.

Redefining Methods

As mentioned, an important aspect we need to see within the ABAP Objects is the redefinition of inherited methods defined in the subclasses.

An instance method inherited from a superclass may be redefined in the subclasses. This allows you to add more specific functionality to the redefined method in the subclass (or subclasses).

During redefinition, you may not change the visibility section of the method. For example, it is not possible to have a private method redefined as a public method in a subclass. The signature of the instance method also cannot be changed during the redefinition. For example, it is not possible to add a new parameter to the method in the definition or change the type of a given parameter.

It is also important to note that static methods cannot be redefined in subclasses. Only instance methods can be redefined.

Within a local subclass, we may redefine a given instance method using the REDEFINITION keyword. Let’s take a look at an example of this.
class football_player DEFINITION INHERITING
      FROM player.
    public section.
      methods display_player_details REDEFINITION.
    ........
endclass.

It is possible to call the superclass’ method DISPLAY_PLAYER_DETAILS within the redefined method using SUPER->DISPLAY-PLAYER_DETAILS .

Instance Constructors

As discussed, every class has an instance constructor. Within the inheritance tree, the various instance constructors (of a class, its superclasses, and subclasses) are independent of each other. You may not redefine an instance constructor of a class in its subclasses.

The instance constructor is called automatically upon instantiation (creation) of the object (via the CREATE OBJECT statement). Within the constructor of the subclass, the immediate superclass constructor may be called. You may call the constructor of a superclass from a subclass using the following statement:
CALL METHOD SUPER->CONSTRUCTOR .......

This statement must not be used in the class that’s a direct subclass of the OBJECT class.

After the SUPER->CONSTRUCTOR call, the code for assigning values to the attributes specific to the subclass is written, as shown in the following example.
class football_player IMPLEMENTATION.
      method constructor.
        call method super->constructor
           exporting
             name = name
             weight = weight
             height = height.
        me->football_club = football_club.
      endmethod .
endclass.
When you instantiate a class that has an instance constructor with a set of parameters (interface), appropriate values must be then supplied using the EXPORTING addition within the CREATE OBJECT statement:
create object myfootball_player
          exporting name =  'Oliver Kahn'
                    weight = '95'
                    height =  '188'
                 football_club = 'Bayern Munich'.

Likewise, when you call a superclass constructor from a subclass, appropriate values must be supplied for the various parameters (mandatory, non optional). A class that is instantiated at the bottom of the inheritance tree may need to pass parameters to the class constructor that is close to the root.

Note

If a given SUPERCLASS has an instance constructor that contains the call of a method via the self-reference variable me, then the method as defined in the SUPERCLASS is called and not the ones redefined in any of the subclasses.

Working Example

In this section, we look at how these concepts can be applied to solve a simple requirement.

We create a superclass player and its subclass Football_player. A suitable Football_player object is created and its attributes are displayed onscreen.

We first define a PLAYER class having three attributes in the protected section, including NAME, HEIGHT, and WEIGHT. Two methods are defined within the public section—the instance constructor and the DISPLAY_PLAYER_DETAILS method. Appropriate parameters are specified for the CONSTRUCTOR for creating instances belonging to the PLAYER class. The DISPLAY_PLAYER_DETAILS method has no parameters. The definition of the class looks like this:
class player DEFINITION.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i.
       methods display_player_details.
    protected section.
      data : name type string.
      data : height type i.
      data : weight type p decimals 2.
endclass.
Within the implementation section of the PLAYER class, we write the code for constructor, by initializing the attributes NAME, HEIGHT, and WEIGHT. Within the DISPLAY_PLAYER_DETAILS method, we include the WRITE statement to display the attribute values on the user screen.
class player IMPLEMENTATION.
      method constructor .
         me->name = name.
         me->height = height.
         me->weight = weight.
      ENDMETHOD.
 method display_player_details.
   write :/  'Player Name:   ',15 me->name ,
         /  'Height     :    ',15  me->height
                  LEFT-JUSTIFIED ,
                    /  'Weight     :',15 me->weight
                        LEFT-JUSTIFIED.
               ENDMETHOD.
             endclass.

Next, we define the FOOTBALL_PLAYER class. The constructor of the class takes as an importing parameter the player’s name, weight, and height, as well as the name of the football club. The private section contains the FOOTBALL_CLUB attribute—this is specific to the football player class.

A redefined method for displaying the player’s detail is also DISPLAY_PLAYERS_DETAILS defined.
class football_player DEFINITION INHERITING
      FROM player.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i
                         football_club type string.
      methods display_player_details REDEFINITION.
    private section.
      data : football_club type string.
endclass.
Within the implementation of the class, we have the code written for the subclass constructor and DISPLAY_PLAYER_DETAILS .
class football_player IMPLEMENTATION.
      method constructor.
        call method super->constructor
           exporting
             name = name
             weight = weight
             height = height.
      me->football_club = football_club.
  endmethod .
method display_player_details.
       call method super->display_player_details.
       write:/ 'Club Name  :       ',15
                    me->football_club.
 endmethod .
 endclass.
The full code of the example is shown here:
class player DEFINITION.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i.
       methods display_player_details.
    protected section.
      data : name type string.
      data : height type i.
      data : weight type p decimals 2.
endclass.
class player IMPLEMENTATION.
      method constructor .
         me->name = name.
         me->height = height.
         me->weight = weight.
      ENDMETHOD.
 method display_player_details.
   write :/  'Player Name:   ',15 me->name ,
         /  'Height     :    ',15  me->height
            LEFT-JUSTIFIED ,
        /  'Weight     :',15 me->weight
           LEFT-JUSTIFIED.
 ENDMETHOD.
endclass.
class football_player DEFINITION INHERITING
      FROM player.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i
                         football_club type string.
      methods display_player_details REDEFINITION.
    private section.
      data : football_club type string.
endclass.
class football_player IMPLEMENTATION.
      method constructor.
        call method super->constructor
           exporting
             name = name
             weight = weight
             height = height.
             me->football_club = football_club.
      endmethod .
      method display_player_details.
       call method super->display_player_details.
       write:/ 'Club Name  :       ',15
         me->football_club.
      endmethod .
   endclass.
   START-OF-SELECTION.
   data : myfootball_player type ref to
          football_player.
   create object myfootball_player
          exporting name =  'Oliver Kahn'
                    weight = '95'
                    height =  '188'
                  football_club = 'Bayern Munich'.
 call method
 myfootball_player->display_player_details.

We have defined a reference variable typed with the FOOTBALL_PLAYER class. We then created the football player object using the CREATE OBJECT statement. Appropriate values are supplied for instantiation of the football player object. The values exported from the program to the CREATE OBJECT statement are imported into the constructor. First, the constructor of the FOOTBALL_PLAYER class is executed, within which the constructor of the superclass PLAYER is called. The code of the super->constructor assigns values to the NAME, HEIGHT, and WEIGHT attributes, whereas the last line of the subclass’ constructor assigns a supplied value to the FOOTBALL_CLUB attribute.

Once the football player object is created, we called the DISPLAY_PLAYER_DETAILS method using the reference variable FOOTBALL_PLAYER. The method contains a call to the SUPER->DISPLAY_PLAYER_DETAILS method, which displays the three attribute values—player name, height, and weight. The football club name is printed via the WRITE statement within the redefined DISPLAY_PLAYER_DETAILS method of the football player class.

The output of the program is shown in Figure 3-4.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig4_HTML.jpg
Figure 3-4

Program output

Casting and Polymorphism

Related to the concept of inheritance is casting. Within the arena of inheritance and ABAP Objects, upcasting and downcasting are both possible. Let’s take a closer look at these concepts.

Using upcasting , a reference variable typed with reference to a subclass may be assigned to a variable typed via reference to its direct superclass (or any of its superclasses).
 superclass_type =  subclass_type.   "Upcasting

This statement is permissible because a subclass contains all components of all its superclasses. Moreover, the interfaces of methods of a superclass cannot be changed in subclasses. The reference variable SUPERCLASS_TYPE may then be used to address the components visible to the given superclass. In this case, no specific functionality added to the subclasses may be addressed. For example, any new attribute added to the subclasses, or any new method added to the subclasses, may not be accessed using the variable SUPERCLASS_TYPE. (We will look at a working example of upcasting and its role in implementing polymorphism in the latter part of this section.)

On the other hand, downcasting involves assignment of a reference variable (based on a reference of a superclass) to a reference variable typed via reference to a subclass:
subclass_type ?= superclass_type.  "Downcasting

In this case, the downcasting operator ?= must be used. If we use the equals (=) sign instead, a syntax error will occur at the time of program check.

Note

In upcasting, we assign a reference variable to a more general reference variable type; i.e we go up the inheritance hierarchy. On the other hand, in the case of downcasting, the target is a more specific type (i.e., we move downward within the inheritance tree).

Downcasting is particularly useful when we need to access components. For example, when we have a method that is defined in the more specific subclass, but we have a reference to superclass and not to the subclass itself. Let’s now consider the following example (pertaining to ALV classes) in order to understand downcasting and how to use it.
DATA: ALV TYPE REF TO CL_SALV_TABLE.
DATA: MYCOLUMNS TYPE REF TO CL_SALV_COLUMNS_TABLE.
DATA: ONE_COLUMN TYPE REF TO CL_SALV_COLUMN_TABLE.
....
....
 MYCOLUMNS = ALV->GET_COLUMNS( ).
 ONE_COLUMN ?= MYCOLUMNS->GET_COLUMN( 'COLNAME' ).

The GET_COLUMN method returns an instance of the CL_SALV_COLUMN class, but we need to call the methods of the CL_ALV_COLUMN_TABLE class. For example, the SET_KEY method used for setting a column as a key field is available in the CL_SALV_COLUMN_TABLE class and not in CL_SALV_COLUMN. (The CL_SALV_COLUMN class is a superclass (not direct) of the CL_SALV_COLUMN_TABLE class.)

In this case, downcasting is useful. The returned reference is assigned to the ONE_COLUMN variable, based on CL_SALV_COLUMN_TABLE. Once the downcasting is successful, we can call the methods that are provided via the CL_SALV_COLUMN_TABLE class using the reference variable ONE_COLUMN. To catch any exceptions that occur during casting, we can use the exception class CX_SY_MOVE_CAST_ERROR.

As an example, let’s add another class called Cricket_player to our PLAYER and FOOTBALL_PLAYER example. Two objects are created, one for Football_player and the other for the Cricket_player class. Their details are printed using a single reference variable, based on superclass PLAYER.

If you redefine a given instance method in multiple subclasses, it is possible to access the various method implementation via one reference variable, based on the superclass type. Addressing the different implementations using a single reference variable is called polymorphism . Upcasting may be used to implement polymorphism.

We declare another class, CRICKET_PLAYER, that is also inherited from the class PLAYER. We add a new attribute called COUNTY_NAME with type STRING. The constructor of this class is also added with COUNTY_NAME as an importing parameter. As with the FOOTBALL_PLAYER class previously shown, the DISPLAY_PLAYER_DETAILS method is redefined in the class. The definition of this class is shown here:
class cricket_player definition inheriting
      from player.
  public section.
    methods constructor importing
                        name type string
                        weight type p
                        height type i
                        county_name type string.
    methods display_player_details redefinition.
  private section.
    data : county_name type string.
endclass.

Next, within the constructor in the CRICKET_PLAYER implementation, we call SUPER->CONSTRUCTOR and assign the importing parameter. COUNTY_NAME to the private attribute COUNTY_NAME defined in the class.

In the redefined method DISPLAY_PLAYER_DETAILS , we add a WRITE statement that displays the county for which the cricketer plays, that is the COUNTY_NAME.
class cricket_player implementation.
  method constructor.
    call method super->constructor
      exporting
        name   = name
        weight = weight
        height = height.
    me->county_name = county_name.
  endmethod .
  method display_player_details.
    call method super->display_player_details.
    write:/ 'County Name  :       ',
    15 me->county_name.
  endmethod .
endclass.
We then create two objects belonging to the FOOTBALL_PLAYER and CRICKET_PLAYER classes respectively, using CREATE OBJECT statements .
data : myfootball_player type ref to football_player.
  create object myfootball_player
    exporting
      name          = 'Oliver Kahn'
      weight        = '95'
      height        = '188'
      football_club = 'Bayern Munich'.
data : mycricket_player type ref to cricket_player.
  create object mycricket_player
    exporting
      name        = 'James Reed'
      weight      = '98'
      height      = '178'
      county_name = 'Surrey'.
We now declare a reference variable based on the PLAYER class (the superclass of the FOOTBALL_PLAYER and CRICKET_PLAYER classes defined earlier). We then use upcasting to assign the created object MYFOOTBALL_PLAYER to the PLAYER variable. The method of DISPLAY_PLAYER_DETAILS is then called using the reference variable PLAYER. This is repeated for the CRICKET_PLAYER class. The code for this follows:
  data : player type ref to player.
  player = myfootball_player.
  player->display_player_details( ).
  player = mycricket_player.
  player->display_player_details( ).
The output of the program is shown in Figure 3-5.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig5_HTML.jpg
Figure 3-5

Program output

The complete listing for the program exhibiting polymorphism is as follows:
class player definition.
  public section.
    methods constructor importing
                        name type string
                        weight type p
                        height type i.
    methods display_player_details.
  protected section.
    data : name type string.
    data : height type i.
    data : weight type p decimals 2.
endclass.
class player implementation.
  method constructor .
    me->name = name.
    me->height = height.
    me->weight = weight.
  endmethod.
  method display_player_details.
    write :/  'Player Name:   ',15 me->name ,
           /  'Height     :    ',
           15   me->height left-justified ,
           /  'Weight     :',
           15 me->weight left-justified.
  endmethod.
endclass.
class football_player definition inheriting
      from player.
  public section.
    methods constructor importing
                        name type string
                        weight type p
                        height type i
                        football_club type string.
    methods display_player_details redefinition.
  private section.
    data : football_club type string.
endclass.
class football_player implementation.
  method constructor.
    call method super->constructor
      exporting
        name   = name
        weight = weight
        height = height.
    me->football_club = football_club.
  endmethod .
  method display_player_details.
    call method super->display_player_details.
    write:/ 'Club Name  :       ',
    15 me->football_club.
    skip.
  endmethod .
endclass.
class cricket_player definition inheriting
      from player.
  public section.
    methods constructor importing
                        name type string
                        weight type p
                        height type i
                        county_name type string.
    methods display_player_details redefinition.
  private section.
    data : county_name type string.
endclass.
class cricket_player implementation.
  method constructor.
    call method super->constructor
      exporting
        name   = name
        weight = weight
        height = height.
    me->county_name = county_name.
  endmethod .
  method display_player_details.
    call method super->display_player_details.
    write:/ 'County Name  :       ',
    15 me->county_name.
  endmethod .
endclass.
start-of-selection.
data : myfootball_player type ref to football_player.
  create object myfootball_player
    exporting
      name          = 'Oliver Kahn'
      weight        = '95'
      height        = '188'
      football_club = 'Bayern Munich'.
data : mycricket_player type ref to cricket_player.
  create object mycricket_player
    exporting
      name        = 'James Reed'
      weight      = '98'
      height      = '178'
      county_name = 'Surrey'.
  data : player type ref to player.
  player = myfootball_player. “UPCASTING
  player->display_player_details( ).
  player = mycricket_player.
  player->display_player_details( ).

Global Subclasses and Redefinition of Methods

We have discussed how to define and implement subclasses locally within a program. In this section, we learn how to define global classes that are derived from other global classes (superclasses). Let’s take a look at how to do this.

We will define a class ZST6_FOOTBALL_PLAYER_CLASS (a football player class) that is derived from the superclass ZST6_PLAYER_CLASS. For the sake of this example, we assume that the superclass ZST6_PLAYER_CLASS (the PLAYER class) already exists.

The global PLAYER class is similar to the one used as the superclass in the previous example of our locally defined class. One important point to keep in mind is that the final checkbox on the Properties tab must not be selected (see Figure 3-6). This is because we will have to define the FOOTBALL_PLAYER subclass based on the PLAYER class.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig6_HTML.jpg
Figure 3-6

Player class

Three attributes (NAME, WEIGHT, and STRING) with protected visibility are defined for the PLAYER class, as shown in Figure 3-7.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig7_HTML.jpg
Figure 3-7

Attributes of the player class

An instance constructor and a DISPLAY_PLAYER_DETAILS method are also defined. The code of the constructor of the PLAYER class is shown here.
  method CONSTRUCTOR.
         me->name = name.
         me->height = height.
         me->weight = weight.
  endmethod.
The DISPLAY_PLAYER_DETAILS method will remain the same as shown earlier in the local class example:
  method DISPLAY_PLAYER_DETAILS.
       write :/  'Player Name:   ',15 me->name ,
         /  'Height     :    ',15  me->height
            LEFT-JUSTIFIED ,
        /  'Weight     :',15 me->weight
           LEFT-JUSTIFIED.
 ENDMETHOD.

Now comes the most important part. We will see how the inherited class is defined.

Call transaction SE24. Enter the name of the new ZST6_FOOTBALL_PLAYER_CLASS in the field provided. Then click the Create button. Choose the option class from the small popup box that appears and then click the Continue button. This will display the dialog box shown in Figure 3-8.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig8_HTML.jpg
Figure 3-8

Creating the football player class

Enter the description in the field provided. Then click the Save button. This will take you to the screen shown in Figure 3-9.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig9_HTML.jpg
Figure 3-9

Properties of the football player class

On the Properties tab, click the ../images/478428_1_En_3_Chapter/478428_1_En_3_Figa_HTML.jpg button. This will make the superclass field visible and ready for input. Enter the name of the superclass in the field provided (in our case, ZST6_PLAYER_CLASS). Then press Enter to save your entries.

By saving your class, you automatically fill in the Attributes and Methods tabs from the superclass ZST6_PLAYER_CLASS, as shown in Figure 3-10.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig10_HTML.jpg
Figure 3-10

Attributes tab

The protected attributes of the superclass automatically appear in the Attributes tab of the subclass. Likewise, the public methods defined within the superclass are made available as methods of the inherited class (see Figure 3-11).
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig11_HTML.jpg
Figure 3-11

Methods tab

We now enter our subclass-specific FOOTBALL_CLUB attribute on the STRING type and include the necessary description on the Attributes tab, as shown in Figure 3-12.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig12_HTML.jpg
Figure 3-12

Football club attribute

We now have a total of four attributes in the subclass (NAME, WEIGHT, HEIGHT, and FOOTBALL_CLUB).

Next, we need to define the instance constructor of our subclass FOOTBALL_PLAYER. Click the Constructor button ../images/478428_1_En_3_Chapter/478428_1_En_3_Figb_HTML.jpg . This will display the dialog box shown in Figure 3-13.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig13_HTML.jpg
Figure 3-13

Creating a constructor

Click the Yes button, as we need the parameters of the superclass’ constructor for the instance constructor of our inherited class. On the parameters list, the NAME, WEIGHT, and HEIGHT parameters will be added. Then, we will add the fourth parameter in the list of importing parameters of our instance constructor, as shown in Figure 3-14.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig14_HTML.jpg
Figure 3-14

Parameters of subclass instance constructor

Within the code of the constructor, the superclass constructor is called, as well as a statement included to assign the value imported via FOOTBALL_CLUB to the corresponding attribute of the class. The code is shown in Figure 3-15.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig15_HTML.jpg
Figure 3-15

Constructor code

Next, we need to redefine the DISPLAY_PLAYER_DETAILS method in our inherited class. We need to make the method “specific” to the football player class in order for it to print the name of the football club that the player is associated with.

To redefine a method, while on the Methods tab, select the DISPLAY_PLAYER_DETAILS method and click the ../images/478428_1_En_3_Chapter/478428_1_En_3_Figc_HTML.jpg button.

You will then be taken to the code editor. Add the following code to the redefined method:
 method DISPLAY_PLAYER_DETAILS.
   CALL METHOD SUPER->DISPLAY_PLAYER_DETAILS.
   write:/ 'Club Name  :       ',15
         me->football_club.
  endmethod.

Once you are done, activate your subclass.

After activation, the class may be used in your ABAP programs. Objects may be instantiated based on the newly created subclass ZST6_FOOTBALL_PLAYER_CLASS. The code for doing so is shown here:
data : myfootball_player type ref
            to ZST6_FOOTBALL_PLAYER_CLASS.
   create object myfootball_player
            exporting name =  'Oliver Kahn'
                        weight = '95'
                        height =  '188'
                        football_club = 'Bayern Munich'.
   call method myfootball_player->display_player_details.

This will create an object for the football player and print its details, as shown earlier.

Interfaces

In ABAP Objects, interfaces are also of importance. Interfaces may be simply defined as a set of components (events, attributes, and methods) that may be reused (implemented) in classes, thus may be used to extend the scope of classes. Interfaces cannot have instances (unlike classes), but are only used to implement classes.

Interfaces only have a public section and have no implementation part. The implementation (of the interface’s methods) is done in the various classes that implement the interface.

You may have one class implement more than one interface. Also one interface may be used in multiple classes. For example, we may have an interface called INT_PLAYER that’s used in the FOOTBALL_PLAYER and CRICKET_PLAYER classes. The interfaces may only be used in the public section of the class that implements them.

They may not be used in the private or protected sections. An interface may be composed of one or more interfaces—these are called composite interfaces . We use the following syntax to create an interface locally within a program:
INTERFACE int_name.
......
ENDINTERFACE.
For example, say we have an INTERFACE with the same DISPLAY comprised of a DISPLAY_PLAYER_DETAILS method, as shown here:
INTERFACE display.
  METHODS display_player_details.
ENDINTERFACE.

The particular interface may then be used in a class using the INTERFACES keyword within the public section of the class definition (as mentioned earlier).

Suppose we use this interface to extend the PLAYER class. The following code shows how this is done:
class player DEFINITION.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i.
      INTERFACES display.  ""INTERFACE used
    private section.
      data : name type string.
      data : height type i.
      data : weight type p decimals 2.
endclass.
Within the implementation of the PLAYER class , we have the code of the DISPLAY_PLAYER_DETAILS method, as shown here:
method display~display_player_details.
   write :/  'Player Name:   ',15 me->name ,
         /  'Height     :    ',15  me->height
            LEFT-JUSTIFIED ,
        /  'Weight     :',15 me->weight
           LEFT-JUSTIFIED.
 ENDMETHOD.

You may note how the name of the method is indicated, i.e., the name of the interface followed by the method name, separated by a tilde (~). The tilde is also known as the Interface component selector.

Suppose we have a class called myclass that has implemented an interface called myinterface, and the variable classref points to an object in the myclass class. Then, we can have an assignment:
    intref = classref.

After this, the interface reference in intref will point to the object as the class reference in classref.

To call the relevant interface methods from a program, there are two possible approaches:
  • We declare two reference variables, one for the PLAYER class and the other for the display interface. The MY_PLAYER object is then created. We assign the MY_PLAYER object reference to the MY_DISPLAY interface reference. The relevant method can then be called to display the player’s details on the user screen.

    data : my_display type ref to display.
    data : my_player type ref to player.
    create object my_player
           exporting name =  'Oliver Kahn'
                     weight = '95'
                     height =  '188'.
    my_display = my_player.
    my_display->display_player_details( ).
  • Alternatively, we declare only one variable, called MY_DISPLAY, based on the PLAYER class. An object is created via the CREATE OBJECT statement. We then use the MY_PLAYER reference and the interface component selector (~) to call the DISPLAY_PLAYER_DETAILS method.

    data : my_display type ref to player.
    create object my_player
           exporting name =  'Oliver Kahn'
                     weight = '95'
                     height =  '188'.
     my_player->display~display_player_details( ).
The result of this code is the same (i.e., the player details are displayed on the user screen) as shown in Figure 3-16.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig16_HTML.jpg
Figure 3-16

Program output

The complete listing of the program is shown here (the first approach is shown):
INTERFACE display.
  METHODS display_player_details.
ENDINTERFACE.
class player DEFINITION.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i.
      INTERFACES display.
    private section.
      data : name type string.
      data : height type i.
      data : weight type p decimals 2.
endclass.
class player IMPLEMENTATION.
      method constructor .
         me->name = name.
         me->height = height.
         me->weight = weight.
      ENDMETHOD.
 method display~display_player_details.
   write :/  'Player Name:   ',15 me->name ,
         /  'Height     :    ',15  me->height
            LEFT-JUSTIFIED ,
        /  'Weight     :',15 me->weight
           LEFT-JUSTIFIED.
 ENDMETHOD.
endclass.
   START-OF-SELECTION.
   data : my_display type ref to display.
   data : my_player type ref to player.
   create object my_player
          exporting name =  'Oliver Kahn'
                    weight = '95'
                    height =  '188'.
   my_display = my_player.
   my_display->display_player_details( ).
my_player->display~display_player_details( ).

Creating Global Interfaces

To define global interfaces using the Class Builder, follow these steps.
  1. 1.
    Call transaction SE24. Enter the name of the interface in the field provided (in our case, Z_INTERFACE_PLAYER) and click the Create button. The dialog box shown in Figure 3-17 appears.
    ../images/478428_1_En_3_Chapter/478428_1_En_3_Fig17_HTML.jpg
    Figure 3-17

    Choosing the interface object type

     
  2. 2.
    In this case, select the Interface checkbox and click the Continue button. The popup box appears, as shown in Figure 3-18.
    ../images/478428_1_En_3_Chapter/478428_1_En_3_Fig18_HTML.jpg
    Figure 3-18

    The Create Interface dialog box

     
  3. 3.
    Enter the description of the interface in the field provided and click the Save button. This will take you to the screen shown in Figure 3-19.
    ../images/478428_1_En_3_Chapter/478428_1_En_3_Fig19_HTML.jpg
    Figure 3-19

    Adding methods to the interface

     
  4. 4.

    On the Methods tab, add the DISPLAY_PLAYER_DETAILS method as an Instance Method level with an appropriate description. Save and activate your interface.

     
  5. 5.
    To use the global interface in a global class definition, click on the Interfaces tab and enter the name of the interface, as shown in Figure 3-20.
    ../images/478428_1_En_3_Chapter/478428_1_En_3_Fig20_HTML.jpg
    Figure 3-20

    Using an interface in a global class

     
  6. 6.
    Once you save your class, the Methods tab will be populated with the Interface method (Z_INTERFACE_DISPLAY~DISPLAY_PLAYER_DETAILS). See Figure 3-21.
    ../images/478428_1_En_3_Chapter/478428_1_En_3_Fig21_HTML.jpg
    Figure 3-21

    Interface method added

     
  7. 7.

    You can double-click on the method name. This will take you to the editor to add the method code. Then, you can activate the class in question.

     
A global interface can be used in global classes and global interfaces as well as within local interfaces and local classes. For example, we may use our newly-defined Z_INTERFACE_DISPLAY in order to extend our PLAYER class, as shown here:
class player DEFINITION.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i.
      INTERFACES Z_INTERFACE_DISPLAY. "global
                 " interface Z_INTERFACE_DISPLAY .

Abstract and Final Classes

In this section, we discuss the two important types of classes that can be defined locally or globally using the Class Builder. You can create classes as abstract or as final.

With abstract classes, you may not create any instances. Abstract classes are defined using the addition ABSTRACT within DEFINITION, as shown in the following syntax.
CLASS MYABSTRACTCLASS DEFINITION ABSTRACT.
ENDCLASS.
An example of an abstract class player is shown below:
class player DEFINITION abstract.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i.
      methods display_player_details.
     PROTECTED SECTION.
      data : name type string.
      data : height type i.
      data : weight type p decimals 2.
endclass.
If you try to create an instance of an abstract class, a syntax error results, as shown in Figure 3-22.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig22_HTML.jpg
Figure 3-22

Syntax error on abstract class instantiation

With global classes, you can define an abstract class by choosing abstract as the instance generation field on the Create Class dialog, as shown in Figure 3-23.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig23_HTML.jpg
Figure 3-23

The Create Class dialog

The same field may be edited from the Properties tab, as shown in Figure 3-24.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig24_HTML.jpg
Figure 3-24

Properties tab

Note

Abstract classes cannot be instantiated, but can be inherited. Final classes cannot be inherited, but can be instantiated.

Final classes are classes that may not be inherited, i.e. they cannot have subclasses. They may be defined locally in a program or globally using the Class Builder. A final class is specified locally via the addition FINAL, as shown here:
class football_player DEFINITION INHERITING
      FROM player final.
    public section.
      methods constructor importing
                          name type string
                          weight type p
                          height type i
                        football_club type string.
      methods display_player_details REDEFINITION.
    private section.
      data : football_club type string.
endclass.
With global classes, you need to check the Final checkbox on the Properties tab, as shown in Figure 3-25.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig25_HTML.jpg
Figure 3-25

Final checkbox on the Properties tab

If you try to derive a class from a FINAL class, a syntax error occurs, as shown as in Figure 3-26.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig26_HTML.jpg
Figure 3-26

Error on final class

Friendships

Another important concept within ABAP Objects is the concept of friend classes . From outside the class, you can only access the public components of the class in question. The protected components are only visible to the subclasses of the given class.

In some very special cases, it may be required to grant another class access to a class’ private components. This is done by designating the class as a friend (class) to the one whose private components are to be accessed. In this way, only friends may have access to these invisible components. This does not grant access to any other classes.

A class A may be specified as a friend to a given class. In addition, you may specify an interface I as a friend. In this way, all classes that implement the friend interface are friends of the given class and may access its private, public, and protected components.

Note

Friends are given access to all components of the class that offers the friendship, irrespective of the visibility section.

Friendship defined between two classes is one-way. For example, if the class C1 has a friend F1 specified in the definition, then the class C1 is not automatically the friend of F1. If the class C1 wants to access the protected or private components of its friend F1, then the latter must explicitly grant friendship to C1.

In addition, the friendship is not hereditary, i.e., a superclass’ friend is not a friend of its subclasses. On the other hand, subclasses of a friend class are implicitly the friends of the given class that granted the friendship. For example, consider the following scenario.

Suppose we have a class C1 that has a friend F1 that has subclasses F2 and F3. Once class C1 has granted friendship to F1, F2 and F3 automatically become friends of C1 and can access private (and protected) components of C1.

You must therefore be very cautious when granting friendship to a class. A high-position friend class within an inheritance hierarchy means that a number of subclasses can access the components of the class that granted friendship. In an ideal situation, you may specify a final class as a friend class, which ensures that no other classes are granted implicit friendship.

In order to specify the friend of a particular local class, the FRIENDS addition is used in the class definition. After the FRIENDS keyword, the various classes and interfaces (to which the friendship is granted) are listed. Friendship can be granted to all classes or interfaces of the same program.

In addition to accessing the components of a class, friend classes can also create instances of the given class that granted friendship, irrespective of whether private instantiation has been specified in the class definition.

To specify a particular global class as a friend of another class, you must enter the name of the former on the Friends tab, as shown in Figure 3-27.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig27_HTML.jpg
Figure 3-27

Friends tab

Let’s now look at how a friendship can be created between classes. (We will look at an example of a local friend class shortly.) Consider the following piece of code:
class friend1 definition.
  public section.
    methods display.
endclass.
class myclass definition create private friends friend1.
  private section.
    data private_int type i value 200.
endclass.
class friend1 implementation.
  method display.
    data myobject type ref to myclass.
    create object myobject.
    write : myobject->private_int.
  endmethod.
endclass.
start-of-selection.
  data friend1 type ref to friend1.
  create object friend1.
  friend1->display( ).
We created a class called FRIEND1 with a display method in its definition. Another class, called MYCLASS, has a private instantiation defined with FRIEND1 specified as its friend. This class has a private attribute PRIVATE_INT with an initial value of 200. Within the DISPLAY method of the FRIEND1 class, an object called MYCLASS is created using a CREATE OBJECT statement and the value of the private attribute PRIVATE_INT is then printed. Finally, we define a reference to the FRIEND1 class and create a corresponding object. The DISPLAY method is then called. The DISPLAY method creates an object of MYCLASS and displays its private attribute value 200 on the user screen. If the FRIEND addition is not specified, the following syntax error occurs when trying to create an instance of MYCLASS within the DISPLAY method of FRIEND1. See Figure 3-28.
../images/478428_1_En_3_Chapter/478428_1_En_3_Fig28_HTML.jpg
Figure 3-28

Error related to private instantiation

Summary

In this chapter, we learned about inheritance and redefining of methods. We then covered instance constructors and interfaces. A section on casting and polymorphism was also included. We also discussed abstract and final classes. Finally, we discussed the friendship concept within classes, along with looking at fully working coding examples.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.28.179