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

2. Class Components in Detail

Rehan Zaidi1 
(1)
Dubai, United Arab Emirates
 

The first chapter introduced you to the basic concepts of object-oriented ABAP. In this chapter, we continue moving ahead and cover a number of useful topics that you need to know to work with ABAP Objects.

We also cover the concepts of local and global classes along with demos and examples. We will move one more step forward. I will use the global as well as local classes to exemplify my point.

The following topics will be covered in this chapter:
  • CASE TYPE OF used for determining the type of an object reference variable

  • The NEW operator

  • Defining types in classes

  • Inline declaration

  • Specifying constant values within classes

  • Static constructor concept

  • Method chaining and functional methods

  • Event handling

Finding the Type of an Object Reference Variable: Revisited

Depending on the requirements, it may be necessary to confirm if an object belongs to a particular class, i.e, for example, if an EMPLOYEE object is an instance of the ZCL_TEST_EMPLOYEE class.

As mentioned in last chapter, since NetWeaver 7.50, we have a new expression called IS INSTANCE OF that can be used in conjunction with the IF statement to confirm if a particular object belongs to a particular class. The usage is very simple, just like any other expression within an IF statement.

Suppose we have the following code:
data employee type ref to ZCL_TEST_EMPLOYEE.
 create object employee
    EXPORTING
         number  = 10
         name = 'John Reed'
         country = 'England'.

Here, we defined a variable called EMPLOYEE that is a reference to the ZCL_TEST_EMPLOYEE class. We then used the CREATE OBJECT statement to instantiate the given object with suitable attribute values for number, name, and country.

In this case, we use the IF statement with the IS INSTANCE OF addition on the given class, as shown here:
if employee is instance of ZCL_TEST_EMPLOYEE.
   NEW-LINE.
    write : 'This is an employee'.
 endif.
The output of this code is shown in Figure 2-1.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig1_HTML.jpg
Figure 2-1

Program output

There is another way we can determine the type of the given employee.

This is done using the CASE.. ENDCASE control structure. A simple example of this in the context of our employee scenario is as follows:
   CASE TYPE OF employee.
       WHEN TYPE zcl_test_employee.
          write : / 'This is an employee'.
       WHEN OTHERS.
   ENDCASE.

Here, note that the TYPE OF addition has been used along with CASE, and the TYPE variant is written with the WHEN clause. This code also checks whether the employee object is an instance of the ZCL_TEST_EMPLOYEE class. The code output is the same as shown in Figure 2-1.

Using the New Operator to Create an Object

Starting with NetWeaver 7.4, ABAP allows creation of objects via the NEW operator. This operator provides an alternate to the CREATE OBJECT statement. There are a number of ways in which it may be used for objects’ instantiation. The NEW operator may be used for creating instances of both local and global classes.

Before we look at the actual coding, let’s go through the general syntax for the NEW operator. For object creation, the NEW operator may be used in the following two ways:
  1. 1.
    Used in conjunction with the class name. The construct for this is shown here:
    DATA MYOBJ TYPE REF TO MYCLASS.
            MYOBJ = NEW myclass(
                      param1 = val1
                      param2 = val2
                      paramN = valN).
    Using the inline declaration supported by ABAP 7.40, the same form may be written in one single statement:
    DATA(MYOBJ) =    NEW myclass(
                       param1 = val1
                       param2 = val2
                            ...
                       paramN = valN).

    In both of the previous cases, the NEW operator is used in conjunction with the class name. This will create an instance of the MYCLASS class and assign to the reference variable MYOBJ. In the latter case, there is an implicit declaration of MYOBJ based on the MYCLASS class (where the type of MYOBJ is derived from the content of the right side of the assignment). If the constructor of the class has mandatory importing parameters, the corresponding data must be passed to the instance constructor within the parentheses, as shown.

     
  2. 2.
    Used with the # character. Another form of the NEW operator is using it with the # character. The syntax is as follows:
    DATA
       MYOBJ TYPE REF TO MYCLASS.
       MYOBJ =      NEW #(  param1 = val1
                            param2 = val2
                              ...
                            paramN = valN).
     

We declare a variable called MYOBJ referred to the MYCLASS class. Since we already declared a reference variable based on the class in question, there is no need to specify the class again with the NEW operator. Instead of the class name, the # character can be used. Any non-optional parameters are provided in parentheses, as shown earlier.

To better understand how to use the two forms of the NEW operator, let’s look at a few examples. In this example, we use the global football player class ZST6_FOOTBALL_PLAYER_CLASS that we defined earlier in the book.

Consider the following code block, which is a pre-ABAP 7.40 version:
DATA FOOTBALL_PLAYER TYPE REF TO ZST6_FOOTBALL_PLAYER_CLASS. CREATE OBJECT FOOTBALL_PLAYER
"""""""" Without NEW Operator
   EXPORTING
     NAME   = 'Oliver Kahn'
     WEIGHT = '88'
     HEIGHT = '178'
     FOOTBALL_CLUB = 'Bayern Munich'.
FOOTBALL_PLAYER->DISPLAY_PLAYER_DETAILS( ).

We created an object of the global class using the CREATE OBJECT statement. The DISPLAY_PLAYER_DETAILS method was then called to print the details of the created football player. This block of code was earlier used to create a football player object.

We will now see two ways that we can instantiate the object of using the NEW operator (and not the CREATE OBJECT statement). The block of code shown in the previous listing may be replaced with the following:
DATA(FOOTBALL_PLAYER) =
 NEW ZST6_FOOTBALL_PLAYER_CLASS( NAME = 'Oliver Kahn'
                  WEIGHT = '88'
                  HEIGHT = '178'
                  FOOTBALL_CLUB = 'Bayern Munich' ).
FOOTBALL_PLAYER->DISPLAY_PLAYER_DETAILS( ).

We used the inline declaration of FOOTBALL_PLAYER along with the NEW operator in a single statement. This involves the implicit definition of FOOTBALL_PLAYER as a reference of class ZST6_FOOTBALL_PLAYER_CLASS. In this case, we did not have to declare a variable based on the class in a separate statement. An object for the given class is created and the reference variable FOOTBALL_PLAYER may then be used to display the player details.

Let’s now look at another piece of code that uses the NEW operator in conjunction with the # character . The corresponding code is shown here:
DATA: FOOTBALL_PLAYER TYPE REF TO ZST6_FOOTBALL_PLAYER_CLASS.
FOOTBALL_PLAYER = NEW #( NAME = 'Oliver Kahn'
                         WEIGHT = '88'
                         HEIGHT = '178'
                         FOOTBALL_CLUB = 'Bayern Munich').
FOOTBALL_PLAYER->DISPLAY_PLAYER_DETAILS( ).

In this usage form, we declare a reference variable FOOTBALL_PLAYER for the class ZST6_FOOTBALL_PLAYER_CLASS. We use the NEW operator to create an object of the given class. Instead of specifying the name of the class, we use the # character. The reference to the newly created object is assigned to the FOOTBALL_PLAYER variable. The values for the various importing parameters are supplied as shown earlier. The complete class name is not required, as the system automatically detects it from the type of the variable to which the created object’s reference is assigned (in our case, FOOTBALL_PLAYER).

The following block of code pertaining to our requirement will be unacceptable, as the class whose object is to be created is unknown:
DATA(FOOTBALL_PLAYER) =
                   NEW #( NAME = 'Oliver Kahn'
                          WEIGHT = '88'
                          HEIGHT = '178'
                          FOOTBALL_CLUB = 'Bayern Munich' ).
 ""  NOT ALLOWED
FOOTBALL_PLAYER->DISPLAY_PLAYER_DETAILS( ).
In the above code, the error appears as shown in Figure 2-2. An error results since the type (the class name) of the football player object cannot be determined.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig2_HTML.jpg
Figure 2-2

Syntax error using the NEW operator

Defining Our Own Types in Classes

In this section, we learn how to define our own types in local and global classes. You may define types within the class in the private or the public sections (as well as the protected section). We will first see how to define types in local classes.

A simple example of this is as follows:
    class newclass definition.
       public section.
         types : myty_dec2 type p length 13 decimals 2.
     endclass.
 ......

Here we defined our own type based on the public section.

You may define your own variables within the class and outside using the data statement.

Based on a type defined within the public section, it is possible to define variables based on the define type. You may use the class name and the => symbol to address the define type. An example of this follows:
data INT type newclass=>myty_dec2.

As mentioned earlier, it is also possible to define a type in a global class. To define a type in an SE24 transaction, a few additional steps are required.

Go to the class in edit mode. Then, click on the Types tab. Enter the name of the type in the Type field. Enter Public as the visibility for this example. Then click the yellow arrow icon on the relevant row.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig3_HTML.jpg
Figure 2-3

Defining types in SE24

This will take you to the Class Builder code editor, where you may specify the complete specification (length and decimals). This is shown in Figure 2-4.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig4_HTML.jpg
Figure 2-4

Specifying type definition

Save and activate the class.

Constants in Classes

Just like we have constants while making report programs, we can declare constants within local and global classes. Within the realm of classes, constants are special static attributes within a class. Constants may be public or private attributes and may not be changed within the class or outside the class.

For local classes, suppose we have a class called MYCLASS, as shown:
    CLASS myclass DEFINITION.
     PUBLIC SECTION.
     CONSTANTS: myconstant TYPE string VALUE 'XYZ'.
   ENDCLASS.
   DATA myvalue TYPE string.
   myvalue = myclass=>myconstant.
   write myvalue.

Here we have the MYCLASS class, and within the public section, we defined a constant by the name MYCONSTANT and assigned it the value XYZ. Since this is a public attribute, it can be accessed outside the class using the => operator. The value is assigned to the string called myvalue. The contents of myvalue are printed.

Suppose you try to change the value of MYCONSTANT anywhere in the program? An error will occur at the time of the syntax check, as shown in Figure 2-5.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig5_HTML.jpg
Figure 2-5

Syntax error

As mentioned, a constant can be defined in the private section of a class as well. Hence, the following class definition is also acceptable:
CLASS myclass DEFINITION.
     Private SECTION.
       CONSTANTS: myconstant TYPE string VALUE 'XYZ'.
 ENDCLASS.

You may not be able to access myconstant outside the class, like all other private attributes.

It is also possible to define constants within global classes. Let’s see how this is done using the Class Builder. Suppose we have a class called ZCL_TEST_EMPLOYEE (see Figure 2-6).

Here we defined a constant called SALUTATION_MR based on the type STRING, and assigned it the value 'MR'. For the Level, we specified CONSTANT, as shown in Figure 2-6.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig6_HTML.jpg
Figure 2-6

The SALUTATION_MR constant

Dictionary Types for Global Classes

In Chapter 1, we saw how to define a type pertaining to an internal table comprised of employee objects. Earlier we did this by defining a table type local to a program. It is also possible to define a table type globally in the transaction SE24 Class Builder. This may later be used in any program to create objects based on the underlying class. In this section, we see how this is done.

Suppose we have a class called ZCL_TEST_EMPLOYEE, defined in the Class Builder. To create a table type based on this class, follow these steps:
  1. 1.
    Go to transaction SE11. This will display the screen shown in Figure 2-7.
    ../images/478428_1_En_2_Chapter/478428_1_En_2_Fig7_HTML.jpg
    Figure 2-7

    Data type in SE11

     
  2. 2.
    Enter the name of the table type that you want to create in the Data Type field and click the Create button. (As you will see, we called our type ZEMP_OBJECT_TABLE_TYPE.) This will display a small dialog box, as shown in Figure 2-8.
    ../images/478428_1_En_2_Chapter/478428_1_En_2_Fig8_HTML.jpg
    Figure 2-8

    Data type options

     
  3. 3.

    Choose the Table Type option and press Enter. This will take you to the screen shown in Figure 2-9.

     
  4. 4.
    Choose the Reference type option.
    ../images/478428_1_En_2_Chapter/478428_1_En_2_Fig9_HTML.jpg
    Figure 2-9

    Specifying reference type

     
  5. 5.

    Enter the name and description in the fields provided. Choose the Reference Type option and enter the name of the employee class (i.e., ZCL_TEST_EMPLOYEE) in the field as shown. Once you are done, save and activate the type using the CTRL+F3 keys.

     
Now that we are done, we can use this type in any program. An example is shown here:
     data EMPLOYEES type zemp_object_table_type.
This statement is the same as the following two statements:
      Types emp type ref to ZCL_TEST_EMPLOYEE.
       data employees type STANDARD TABLE OF emp.

Static Constructor

In Chapter 1, we discussed instance constructors and saw related coding examples. It is also possible to define static constructors within classes.

A static constructor is also known as a class constructor . As with the other static methods, the static constructor is only able to address the static components residing within the class in question.

We also have a static constructor in a class. This is called ONLY once for a class (and internal session), prior to the first access made to the class.

The following statement shows the generic form of a static constructor declaration within a local class:
CLASS-METHODS class_constructor.

As you will see, we declared a static constructor by the name class_constructor of the class in question. This statement may only be written within the public section of the class (declaration). By default, every class has a built-in class_constructor method in the public section.

However, without the declaration, this constructor is blank.

In contrast to the instance constructor, the static constructor is executed once for each class within an internal session. This execution occurs before the first access to the class in question, such as an instance creation.

You can access any static component of the class using =>. It is also possible to call a static method within the static constructor.

Any failed attempt to dynamically address a nonexistent component of a class does not result in the static constructor being called.

Now that we have some knowledge of the working of static constructors, let’s look at a fully working example:
CLASS my_class DEFINITION.
   PUBLIC SECTION.
     CLASS-METHODS class_constructor.
     CLASS-METHODS my_static_method.
 ENDCLASS.
 CLASS my_class IMPLEMENTATION.
   METHOD class_constructor.
      write : / 'Static Constructor Called'.
   ENDMETHOD.
  METHOD my_static_method.
      write : / 'Static Method Called'.
   ENDMETHOD.
 ENDCLASS.
 START-OF-SELECTION.
   my_class=>my_static_method( ).

Here we have a local class called MY_CLASS that has two static methods, namely CLASS_CONSTRUCTOR and MY_STATIC_METHOD. Within the two methods, there are specific messages outputted in order to show the sequence of method execution. We call the static method only using the component class selector =.>.

Calling the static method MY_STATIC_METHOD will result in the static constructor being executed prior to MY_STATIC_METHOD.

The output of the program is shown in Figure 2-10.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig10_HTML.jpg
Figure 2-10

Program output

Now let’s look at the same requirement fulfilled using a global class. We assume for this example that a class already exists by the name ZCL_TEST_EMPLOYEE.

Follow the steps:
  1. 1.

    Open the class in edit mode. Then click the Methods tab.

     
  2. 2.
    Under the method list, enter the name CLASS_CONSTRUCTOR and press Enter. This will automatically populate Static Method into the Level field, as shown in Figure 2-11.
    ../images/478428_1_En_2_Chapter/478428_1_En_2_Fig11_HTML.jpg
    Figure 2-11

    Methods tab

     

As you will see, the Level is set to Static Method and cannot be changed. In addition, the visibility column will be automatically populated with Public. It is worth noting that if any other visibility, such as Private, is entered in the column, upon pressing Enter, the Visibility will change back to Public.

Method Revisited

In Chapter 1, we saw local as well as global methods. In this section, we will go a few steps further with methods. We will see how to specify internal tables as method parameters, method chaining, and functional methods.

Specifying Internal Tables as Method Parameters

So far we have only seen integers and strings as types of method parameters. It is also possible to specify internal tables as exporting, importing, and returning parameters of class methods.

Let’s now complicate things a bit. In this section, we see how we can specify an internal table as a parameter of the method of a class. In the football player example introduced in Chapter 1, we define a new method, called RETURN_LIST_OF_PLAYERS. Instead of writing the attributes of each player object on the screen, we will return the list of player objects in an internal table.

We will specify an internal table (of player objects) as a parameter to this method. Prior to this method, we specify a type called ty_player_tab based on the the ty_player type defined earlier.
class player DEFINITION DEFERRED.
 types : ty_player type ref to player.
 types : ty_player_tab type STANDARD TABLE OF ty_player.
We then define the static RETURN_LIST_OF_PLAYERS method. The signature of this method is shown in the Player class definition:
class player DEFINITION.
   public section.
    methods write_player_details.
    methods constructor importing name type string
                             country type string
                             club type string.
    class-methods display_list_of_players.
    class-methods return_list_of_players
        exporting players_tab
                 type ty_player_tab.
   private section.
     data name type string.
     data country type string.
     data club type string.
     class-DATA: players_list type STANDARD TABLE OF ty_player.
 ENDCLASS.

As you will see, we have specified an exporting parameter called PLAYERS_TAB based on the TY_PLAYER_TAB type. It must be noted that the table type must be specified here. (This is a TYPE STANDARD TABLE type based on TY_PLAYER.)

Next, we write the code of the method within the Implementation section. Since we already have the internal table of objects stored in the PLAYERS_LIST static variable, we simply use an assignment operator to store the internal table contents in the exporting parameter, called PLAYERS_TAB.
  method return_list_of_players.
     players_tab[] = players_list[] .
   endmethod.

The internal table is then returned as an exporting parameter.

To call the method we will use the player class static method:
player=>return_list_of_players(           IMPORTING players_tab  = data(players_tab) ) .

This method is called after instantiation of two player objects, as shown in our previous example, which shows the contents of our players_tab. We use inline declaration to return this in players_tab.

After execution of the program, the contents of the internal table will look like Figure 2-12.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig12_HTML.jpg
Figure 2-12

Table contents

The complete code of the football player exercise will now look like this:
class player DEFINITION DEFERRED.
 types : ty_player type ref to player.
 types : ty_player_tab type STANDARD TABLE OF ty_player.
 class player DEFINITION.
   public section.
    methods write_player_details.
    methods constructor importing name type string
                             country type string
                             club type string.
    class-methods display_list_of_players.
    class-methods return_list_of_players exporting players_tab type ty_player_tab.
   private section.
     data name type string.
     data country type string.
     data club type string.
 class-DATA: players_list type STANDARD TABLE OF ty_player.
 ENDCLASS.
 class player IMPLEMENTATION .
   method write_player_details.
     skip.
     write :/ 'Name    :', me->name.
     write :/ 'Country :', me->country.
     write :/ 'Club    :', me->club.
   endmethod.
   method constructor.
     me->name =  name.
     me->country = country.
     me->club = club.
     append me to players_list.
   ENDMETHOD.
   method display_list_of_players.
     data : temp_player type ref to player.
     write :/ '------------PLAYERS LIST------------'.
     loop at players_list into temp_player.
       temp_player->write_player_details( ) .
     endloop.
     write :/ '------------------------------------'.
   endmethod.
    method return_list_of_players.
     players_tab[] = players_list[] .
   endmethod.
   endclass.
   start-OF-SELECTION.
   data : player1 type ref to player.
   data : player2 type ref to player.
   CREATE OBJECT player1
     EXPORTING
       name    = 'John Mann'
       country = 'Germany'
       club    = 'Bayern Munich'.
   CREATE OBJECT player2
     EXPORTING
       name    = 'Paul Goldberg'
       country = 'Britain'
       club    = 'Liverpool'.
   player=>return_list_of_players( IMPORTING
               players_tab  =   data(players_tab) ) .
  end-OF-selection.

Inline Declarations While Calling Methods

Since 740, ABAP supports inline declaration of variables. This holds true for method calls as well. We will see how to call methods and specify (as parameters) variables that were not declared earlier in the program.

Consider a situation where we have an employee class called ZCL_EMPLOYEE that has a method called GET_NAME_AND_COUNTRY that returns the name and country of the employee in question.

One of the ways to call this method is:
 data name type string.
 data country type string.
 employee->get_name_and_country(
    IMPORTING
        name = name
        country = country ) .

This is the traditional method, where we first declare variables pertaining to the importing parameters of the method. The value of name and country is then returned in the declared variables.

There is another compact way of writing this using inline declaration. This is shown as:
employee->get_name_and_country(
    IMPORTING
        name = data(name)
        country = data(country) ) .

As you will see, we have not done any prior declaration of the two variables NAME and COUNTRY. Rather we used the feature of inline declaration while specifying the parameters of the method in question. The variable name to be declared inline must be in parentheses and must be preceded by data. The output of this piece of code and the one shown earlier are exactly the same.

Using inline declarations is not just restricted to exporting parameters of the method. They may also be used for RECEIVING parameters (we will discuss this in an upcoming section).

Functional Methods

So far, we have only seen methods that take as input a number of importing parameters and then export a number of parameters (exporting parameters). We also have a special type of method that returns a single result or returning value. Such methods are known as functional methods. These may be defined in local and global classes. It is also possible to have a functional method as a static method or an instance method within a class.

One such functional method is as follows:
class func_method_class DEFINITION.
    public section.
      class-methods calc_average IMPORTING
                           int1 type i
                           int2 type i
                    RETURNING value(average) type i.
 endclass.
 class func_method_class IMPLEMENTATION.
      method calc_average.
        average = ( int1 + int2 )  / 2.
      endmethod.
 endclass.

As you can see, the static method CALC_AVERAGE takes as input two integer values int1 and int2 and returns a single parameter called AVERAGE as the returning parameter. The example shows a local class, called func_method_class. As with other methods, the code of this is written in the IMPLEMENTATION of the class.

To call this method, we use the following formats:
  func_method_class=>calc_average(
           exporting int1 = i1
                     int2 = i2
           RECEIVING average = av ).
As you will see, while calling the CALC_AVERAGE method , we use RECEIVING (corresponding to the RETURNING parameter AVERAGE). In addition, there is another way of calling the functional method using inline declaration:
 func_method_class=>calc_average(
                  exporting int1 = i1
                            int2 = i2
                  RECEIVING average = data(av1) ).
A more meaningful and preferred format is shown in the following example:
  data(i1) = 1.
  data(i2) = 3.
  data(av) =    func_method_class=>calc_average( int1 = i1  int2 = i2 ).
  write : av.

Here we used the assignment operator to return the average result in the variable AV. When using a functional method, you do not have to specify the parameter names for the return value explicitly in the parameter list, as shown in the code.

We can also define a functional method for a global class. We will create a static method called CALC_AVERAGE having public visibility, as shown in Figure 2-13.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig13_HTML.jpg
Figure 2-13

Global class with a functional method

For the method, the next step involves specifying the parameters for the method. For the AVERAGE parameter, we need to select the Returning type. If you do not select the Pass Value checkbox for Average, the system automatically checks this indicator. The other two parameters are specified as Importing, as shown in Figure 2-14.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig14_HTML.jpg
Figure 2-14

Parameters of CALC_AVERAGE

Next, we write the source code of the method, as shown in Figure 2-15.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig15_HTML.jpg
Figure 2-15

Method source code

When you are done, save and activate the class. The method created here may be called in programs the same way the local functional method was called.

A functional method can have only one returning parameter. You may not create two or more returning parameters. You must also ensure that the assignment operator is used with a method that has a returning parameter. If the method does not have a returning parameter, but you try to use it in an expression along with the assignment = operator, a syntax error occurs. Consider the example shown in Figure 2-16.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig16_HTML.jpg
Figure 2-16

Syntax error

Here we have a method called PRIMARY_RECORD_REF that does not have a returning parameter. But we have used this improperly, which results in a syntax error.

When you try to create more than one returning parameter for a given method, a syntax error occurs, as shown in Figure 2-17.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig17_HTML.jpg
Figure 2-17

Syntax error

This error occurs for global as well as local classes. Starting from NetWeaver 7.50, it is also possible to specify exporting parameters for functional methods.

Specifying Exporting and Returning Parameters for Functional Methods

The newer NetWeaver release provides an interesting feature of functional methods. In addition to a returning parameter, a functional method can also have an exporting parameter. However, note that functional methods support only one returning parameter and any number of other formal parameters, including importing and exporting. We can create local or global functional methods, which may be static or instance methods.

In the examples, we cover both scenarios of defining functional methods locally and globally that have both exporting and returning parameters. We use static method in these examples for better understanding.

This first example defines a functional method locally. We define a class with a static method to calculate the total sum and average of two given numbers. The sum will be returned as an exporting parameter, whereas the average will be a returning parameter. The code is shown:
     class newclass definition.
       public section.
         types : myty_dec2 type p length 13 decimals 2.
         class-methods :  func_av_and_total importing
                              myint1 type i
                              myint2 type i
                             exporting
                               sum type i
                             returning
                               value(av) type myty_dec2.
     endclass.

As you see in the code, we defined a class locally within the program. The class name is newclass. A static method, func_av_and_total, is defined within the class, which calculates both the average and the sum of the two numbers supplied to the method.

This method has two import parameters (myint1 and myint2) of type Integer. The sum is the exporting parameter, whereas AV is the returning parameter of the type myty_dec2. (This is a user-defined type defined within the class.)

The class implementation is shown here:
    class newclass implementation.
       method func_av_and_total.
          sum =  myint1 + myint2.
          av = sum / 2.
       endmethod.
     endclass.

In the class implementation, we specified how the sum and the average are calculated. We add the two numbers to get sum and divide the total by 2 in order to get the average that is returned in parameter AV.

We can call this method in multiple ways. The example will show you how to use the assignment operator to read only the average value of the provided numbers.
data(av1) = newclass=>func_av_and_total(
                       exporting  myint1 = '2'
                                  myint2 = '11'  ) .

In the example, a static method is called without creating a class instance. The variable AV is defined using an inline declaration. The method returns the average of the input parameters, which is captured in the variable AV. This may later be printed on the user screen.

In another example, we will show how you call the static method with exporting and importing parameters, along with the returning parameter.
  data sum type i.
  data(av1) = Newclass=>func_av_and_total(
  exporting  myint1 = '2'
             myint2 = '11'
                importing sum =  sum ).
  write : av1 ,  sum.
In this example, the func_av_and_total method is called with exporting integer parameters and an importing sum. The function will return the av value. Here, we have passed numbers 2 and 8 to MYINT1 and MYINT2, respectively. The average value is returned in the AV1 variable, whereas sum is assigned to the variable sum. The output is then printed on the screen, as shown in Figure 2-18.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig18_HTML.jpg
Figure 2-18

Program output

Using Class Builder (transition SE24), we can define a similar method globally. An example of this is shown in Figure 2-19.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig19_HTML.jpg
Figure 2-19

Functional method in sE24

In this class definition, SUM is defined as an exporting parameter and AV is defined as a returning parameter. The ty_dec2 type is defined using the Types tab.

The method may be tested using the standard test feature, as shown in Figure 2-20.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig20_HTML.jpg
Figure 2-20

Test feature

Methods Calling Other Methods

A declared method can call other methods in the source code. For example, it is possible for a local class method to call a (public) method of another local class, or a static local method can call a static global or local method. At the time of calling, the control switches just like other modularization units, such as form subroutine and function modules, and returns to the calling method.

Method Chaining

A newer concept that has evolved is method chaining. This involves combining calls of functional methods (i.e., methods that have one returning value as output). The chaining of methods makes the code more compact and allows you to fulfill requirements without the need for additional reference variables. There are two types of chaining possible: chained method access and chained method call. (The primary emphasis of this section is on chained method access.) Let’s take a look at what each of these means.

In chained method access, there could be two forms as follows:
obj_ref->inst_meth_a(..)->inst_meth_b(..)->inst_meth(..).
class=>static_meth_a(..)->inst_meth_b(..)->inst_meth(..).

The return value of one functional method is a reference to an object that is used to call the next method in the chain. The returning value of the last method is used as an operand.

In this case, the first use may be a class component selector (=>) or an object component selector (->). After that, all the methods are called using the component. Or, in other words, the first method may be a static or an instance method. Other than that, you must have all functional instance methods.

Chained attribute access, on the other hand, is similar to chained method calling. In this case, the instance attribute is used as an operand. The chained attribute access may be in the following forms:
obj_ref->inst_meth_a(..)->inst_meth_b(..)->inst_attr.
class=>static_meth_a(..)->inst_meth_b(..)->inst_attr.

The return value of the last method (INST_METH_B) must refer to the object that contains the INST_ATTR attribute. In this case, the return values of the previous functional methods are variables that refer to objects for the next method.

As an example of method chaining, consider the previous block of code that we used in our ALV example earlier, shown here:
        data : functions type ref to cl_salv_functions.
        functions = alv->get_functions( ).
        functions->set_all( ).
Method chaining can be applied in this case. The three lines above can be replaced by a single line of code, as shown here:
        alv->get_functions( )->set_all( ).

There is also no need to define the FUNCTIONS variable, and the coding is very compact.

Let’s look at one more example of method chaining. Let’s go back to our football example shown earlier involving the NEW operator:
DATA: FOOTBALL_PLAYER type ref to   ZST6_FOOTBALL_PLAYER_CLASS.
   FOOTBALL_PLAYER = NEW #( NAME = 'Oliver Kahn'
                            WEIGHT = '88'
                            HEIGHT = '178'
                            FOOTBALL_CLUB = 'Bayern Munich' ).
   FOOTBALL_PLAYER->DISPLAY_PLAYER_DETAILS( ).
We can combine the NEW operator shown earlier in the chapter with method chaining. The previous block of code can be written more compactly, as shown here:
NEW ZST6_FOOTBALL_PLAYER_CLASS(
        NAME = 'Oliver Kahn'
        WEIGHT = '88'
        HEIGHT = '178'
        FOOTBALL_CLUB =
     'Bayern Munich')->DISPLAY_PLAYER_DETAILS( ).

Event Handling in ABAP Objects

The topic of ABAP Objects is incomplete without one important component that may be included within a class definition—events.

Events are signals generated by a class or its object. They may be the result of changes in the state of an object, such as Employee Hired, Employee Changed, or Player Created.

Just like any other components of a class, there may be static or instance events. Static events are independent of any particular object of a class. Static events may be triggered from both static and instance methods, whereas instance events may be triggered from instance methods only.

At runtime, the handler method’s code (the method that handles the event) is executed when the event is triggered (also known as event raised). Handling and generating events in your programs involves the following essential steps:
  • Defining the method as an instance or static event within the respective class definition. The class containing the event may be a subclass of another class, or may have a number of subclasses derived from it. You may also define an event in an interface. (The event will then be part of the class(es) that implement(s) the interface.) To define the event as static, use this code:
    CLASS_EVENTS <event_name>.
    On the other hand, the following statement defines an instance event:
    EVENTS <event_name>.
  • Raising the event at a suitable place using the RAISE EVENT statement.

    The parameters are specified after the EXPORTING ADDITION. For an instance event, an implicit parameter called SENDER is applicable. This parameter holds a reference to the object for which the event has been generated. For example, when the instance event called PLAYER_CREATED is raised, SENDER will contain the reference to the player object in question. For static events, the SENDER parameter is not there.

  • Defining and implementing the event handler method. This may be defined in the same class or in a separate class. Within the handler class implementation, you write the method code to be executed upon event trigger. The parameters available to the method (i.e., the ones exported via the RAISE statement) may be used by the developer for fulfilling user requirements.

  • Registering the method to react to events. The SET HANDLER statement is used for this purpose. If the event is static, the syntax may be of the form shown here:
    SET HANDLER <method_name>.

    In the case of an instance event, the SET HANDLER statement will contain the additions FOR ALL INSTANCES or FOR (referring to a specific object for whose event the event handler is to be executed). It is possible to specify all instances or a particular object instance whose raised event is to be caught and the handler method is executed.

    This is an important step that defines the link between the handler method and the event that is to be raised. If the registration is not performed, the handler method will not be executed even if the event gets triggered successfully with all essential information passed.

In the next section, we see these steps in detail as applied to our football player example.

A Working Example

In this section, we look at a full-fledged working example of event handling. For the sake of our example, we see how events may be defined and handled in local classes.

Triggering and Handling the PLAYER_CREATED Event

We add the event triggering functionality for our local football player class defined earlier. We create a separate event handler class for handling our PLAYER_CREATED event .

The following steps are required:
  1. 1.
    As mentioned, we need to define the event for our football player class. In our case, we name the event PLAYER_CREATED. This is defined as an instance event, as shown here:
    events : player_created .
     
  2. 2.
    We make sure that the event is defined in the public section of the class. We then raise the event. This is done using the RAISE EVENT statement as shown here:
    raise event player_created.
     
  3. 3.
    We put this in the constructor, as shown here:
     method constructor.
             call method super->constructor
                exporting
                  name = name
                  weight = weight
                  height = height.
             me->football_club = football_club.
             raise event player_created.
    "" nothing exported
    "" but created object passed
    "" via implicit SENDER parameter
      endmethod .
     
  4. 4.

    We will not pass a parameter explicitly to the RAISE event. Next we create an event handler class with the name MYHANDLERCLASS. Within the class, there is a handler method called MYHANDLER defined as a static class method. Within the definition of the class for our handler method, we specify that it is for the PLAYER_CREATED event of the football player class.

     

Note that the importing implicit parameter SENDER is also specified. The sender contains a reference to the object for which the event has been triggered.

The code for the handler class definition is shown here:
  class myhandlerclass DEFINITION.
     public section.
       class-methods myhandler FOR EVENT player_created
       OF football_player importing sender.
   endclass.
Since we defined the event earlier as a public event, we are able to create a handler method in a separate class. Suppose the event was in the private section. In that case, an error would occur, as shown in Figure 2-21 .
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig21_HTML.jpg
Figure 2-21

Syntax error for a private event

Next in the implementation section, we will write the code (for method MYHANDLER) to be executed when the PLAYER_CREATED event is triggered.
  class myhandlerclass IMPLEMENTATION.
    method myhandler.
     write : 'Event Handler Executed'.
     call method sender->display_player_details.
    endmethod.
   endclass.

Within the method code, we will simply call the DISPLAY_PLAYER_DETAILS method of the football player class. This will display the various attributes of the newly created football player.

In this example, we have seen a football player class inherited from a player class. The inheritance concept will be explained in detail in the next chapter.

Next, we will register the handler method to react to the situation when the event occurs. We will use the SET HANDLER statement for this purpose, as shown here:
SET HANDLER myhandlerclass=>myhandler for all instances.

The name of the method along with class name is specified. Since the MYHANDLER is a static method, we use the class component selector (=>). We use the FOR ALL INSTANCES addition in order for the handler method to be executed for all instances of the FOOTBALL_PLAYER class.

When the program is executed, the CREATE OBJECT statement for the football player is executed. Whenever a new football player is created, a Player_Created event is triggered. This is an instance event raised via a RAISE EVENT statement in the constructor of the FOOTBALL_PLAYER class. Since we have registered the MYHANDLER method for the PLAYER_CREATED event of all instances of the football player class, the handler method gets executed. The method receives SENDER as an importing parameter, which is the reference to the football player object for which the event is raised. Within the handler method, we call the DISPLAY_PLAYER_DETAILS method of the FOOTBALL_PLAYER class to display the details of the football player object that is created.

The output of the program is shown in Figure 2-22.
../images/478428_1_En_2_Chapter/478428_1_En_2_Fig22_HTML.jpg
Figure 2-22

Program output

The complete code listing 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.
       events : player_created  .
   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.
         raise event player_created.
       endmethod .
       method display_player_details.
       call method super->display_player_details.
       write:/ 'Club Name  :       ',15 me->football_club.
       endmethod .
    endclass.
   class myhandlerclass DEFINITION.
     public section.
       class-methods myhandler FOR EVENT player_created
       OF football_player importing sender.
   endclass.
   class myhandlerclass IMPLEMENTATION.
    method myhandler.
      write : 'Event Handler Executed'.
      call method sender->display_player_details.
    endmethod.
   endclass.
    START-OF-SELECTION.
    data : myfootball_player type ref to football_player.
    set HANDLER myhandlerclass=>myhandler for all instances.
      create object myfootball_player
                exporting name =  'Oliver Kahn'
                          weight = '95'
                          height =  '188'
                          football_club = 'Bayern Munich'.

Summary

In this chapter, we covered a number of useful topics that you need to know in order to work with ABAP Objects. We discussed the CASE TYPE OF construct used for determining the type of an object reference variable followed by a discussed of the “new” NEW operator.

We also saw how to apply inline declaration in ABAP Objects, constant declaration, method chaining, and functional methods. Finally, we saw a fully working demo of event handling.

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

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