Friday, 19 February 2021

Pass by value or pass by reference?

When calling a method of an ABAP class, data can be passed from actual parameters to formal parameters either by value or by reference. In this blog post I’ll discuss the difference and highlight some important scenarios.

Definitions

Pass by value

The ABAP Keyword Documentation provides the following definition of pass by value:

[…] In pass by value, a local data object is created as a copy of the actual parameter. Output parameters and return values are initialized when the procedure is called. Input parameters and input/output parameters are given the value of the actual parameter when they are called. Modified formal parameters are only passed to actual parameters if the procedure was concluded without errors, that is once the last statement is reached or if there is an exit using RETURN (or EXIT or CHECK).

Pass by reference

The ABAP Keyword Documentation provides the following definition of pass by reference:

[…] Pass by reference does not create a local data object for the actual parameter. Instead a reference to the actual parameter is passed to the procedure when it is called and the procedure works with the actual parameter itself. Input parameters passed by reference cannot be modified in the procedure.

Scenarios

To make the concepts easier to grasp, let us look at a few example scenarios.

The IMPORTING scenario

In the IMPORTING section of the method signature, the keywords VALUE and REFERENCE are used to indicate how the data is passed. If none of these keywords are used, the parameter is passed by reference by default. It is not allowed to change an input parameter passed by reference in the method, whereas this is allowed in the pass by value scenario.

Example:

REPORT zre_test_pass_by_importing.

DATA gv_time TYPE t.

CLASS lcl_time_utility DEFINITION FINAL.

  PUBLIC SECTION.

    CLASS-METHODS display_time

      IMPORTING

        iv_time_by_reference_implicit            TYPE t

        REFERENCE(iv_time_by_reference_explicit) TYPE t

        VALUE(iv_time_by_value)                  TYPE t.

  PROTECTED SECTION.

  PRIVATE SECTION.

ENDCLASS.

CLASS lcl_time_utility IMPLEMENTATION.

  METHOD display_time.

    WRITE: / `Pass by reference (implicit):`.

    WRITE: / iv_time_by_reference_implicit.

    WRITE: / `Pass by reference (explicit):`.

    WRITE: / iv_time_by_reference_explicit.

    WRITE: / `Pass by value:`.

    WRITE: / iv_time_by_value.

    WAIT UP TO 2 SECONDS.

    gv_time = sy-uzeit.

    WRITE: / `Pass by reference (implicit) after 2 seconds wait:`.

    WRITE: / iv_time_by_reference_implicit.

    WRITE: / `Pass by reference (explicit) after 2 seconds wait:`.

    WRITE: / iv_time_by_reference_explicit.

    WRITE: / `Pass by value after 2 seconds wait:`.

    WRITE: / iv_time_by_value.

    iv_time_by_value = iv_time_by_value + 10.

    WRITE: / `Pass by value after adding 10 seconds to the input parameter:`.

    WRITE: / iv_time_by_value.

  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.

  gv_time = sy-uzeit.

  lcl_time_utility=>display_time(

      iv_time_by_reference_implicit = gv_time

      iv_time_by_reference_explicit = gv_time

      iv_time_by_value              = gv_time ).

The report generates the following output to the screen:

Pass by reference (implicit):

132031

Pass by reference (explicit):

132031

Pass by value:

132031

Pass by reference (implicit) after 2 seconds wait:

132033

Pass by reference (explicit) after 2 seconds wait:

132033

Pass by value after 2 seconds wait:

132031

Pass by value after adding 10 seconds to the input parameter:

132041

The EXPORTING scenario

In the EXPORTING section of the method signature, the keywords VALUE and REFERENCE are used to indicate how the data is passed. If none of these keywords are used, the parameter is passed by reference by default. When the parameter is passed by reference, the exporting parameter doesn’t necessarily have an initial value as illustrated by the do_nothing method in the following example. When passing by value, the modified content of the parameter is assigned to the actual parameter only if the method is completed without errors. This is illustrated by the methods get_time_120000_successfully and get_time_120000_with_exception.

REPORT zre_test_pass_by_exporting.

DATA gv_time_by_reference_implicit TYPE t.

DATA gv_time_by_reference_explicit TYPE t.

DATA gv_time_by_value TYPE t.

CLASS lcl_time_utility DEFINITION FINAL.

  PUBLIC SECTION.

    CLASS-METHODS do_nothing

      EXPORTING

        ev_time_by_reference_implicit            TYPE t

        REFERENCE(ev_time_by_reference_explicit) TYPE t

        VALUE(ev_time_by_value)                  TYPE t.

    CLASS-METHODS get_time_120000_successfully

      EXPORTING

        ev_time_by_reference_implicit            TYPE t

        REFERENCE(ev_time_by_reference_explicit) TYPE t

        VALUE(ev_time_by_value)                  TYPE t.

    CLASS-METHODS get_time_120000_with_exception

      EXPORTING

        ev_time_by_reference_implicit            TYPE t

        REFERENCE(ev_time_by_reference_explicit) TYPE t

        VALUE(ev_time_by_value)                  TYPE t

      RAISING

        cx_abap_datfm_invalid_date.

  PROTECTED SECTION.

  PRIVATE SECTION.

ENDCLASS.

CLASS lcl_time_utility IMPLEMENTATION.

  METHOD do_nothing.

  ENDMETHOD.

  METHOD get_time_120000_successfully.

    ev_time_by_reference_implicit = '120000'.

    ev_time_by_reference_explicit = '120000'.

    ev_time_by_value = '120000'.

  ENDMETHOD.

  METHOD get_time_120000_with_exception.

    ev_time_by_reference_implicit = '120000'.

    ev_time_by_reference_explicit = '120000'.

    ev_time_by_value = '120000'.

    RAISE EXCEPTION TYPE cx_abap_datfm_invalid_date.

  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.

  gv_time_by_reference_implicit = sy-uzeit.

  gv_time_by_reference_explicit = sy-uzeit.

  gv_time_by_value = sy-uzeit.

  WRITE: / `Start time:`.

  WRITE: / gv_time_by_value.

  WRITE: / `Do nothing:`.

  lcl_time_utility=>do_nothing(

    IMPORTING

      ev_time_by_reference_implicit = gv_time_by_reference_implicit

      ev_time_by_reference_explicit = gv_time_by_reference_explicit

      ev_time_by_value              = gv_time_by_value ).

  WRITE: / `Pass by reference (implicit):`.

  WRITE: / gv_time_by_reference_implicit.

  WRITE: / `Pass by reference (explicit):`.

  WRITE: / gv_time_by_reference_explicit.

  WRITE: / `Pass by value:`.

  WRITE: / gv_time_by_value.

  WRITE: / `Method call successful:`.

  CLEAR: gv_time_by_reference_implicit, gv_time_by_reference_explicit, gv_time_by_value.

  lcl_time_utility=>get_time_120000_successfully(

    IMPORTING

      ev_time_by_reference_implicit = gv_time_by_reference_implicit

      ev_time_by_reference_explicit = gv_time_by_reference_explicit

      ev_time_by_value              = gv_time_by_value ).

  WRITE: / `Pass by reference (implicit):`.

  WRITE: / gv_time_by_reference_implicit.

  WRITE: / `Pass by reference (explicit):`.

  WRITE: / gv_time_by_reference_explicit.

  WRITE: / `Pass by value:`.

  WRITE: / gv_time_by_value.

  WRITE: / `Method call unsuccessful:`.

  CLEAR: gv_time_by_reference_implicit, gv_time_by_reference_explicit, gv_time_by_value.

  TRY.

      lcl_time_utility=>get_time_120000_with_exception(

        IMPORTING

          ev_time_by_reference_implicit = gv_time_by_reference_implicit

          ev_time_by_reference_explicit = gv_time_by_reference_explicit

          ev_time_by_value              = gv_time_by_value ).

    CATCH cx_abap_datfm_invalid_date.

      " do nothing

  ENDTRY.

  WRITE: / `Pass by reference (implicit):`.

  WRITE: / gv_time_by_reference_implicit.

  WRITE: / `Pass by reference (explicit):`.

  WRITE: / gv_time_by_reference_explicit.

  WRITE: / `Pass by value:`.

  WRITE: / gv_time_by_value.

The report generates the following output to the screen:

Start time:

144905

Do nothing:

Pass by reference (implicit):

144905

Pass by reference (explicit):

144905

Pass by value:

000000

Method call successful:

Pass by reference (implicit):

120000

Pass by reference (explicit):

120000

Pass by value:

120000

Method call unsuccessful:

Pass by reference (implicit):

120000

Pass by reference (explicit):

120000

Pass by value:

000000

The CHANGING scenario

In the CHANGING section of the method signature, the keywords VALUE and REFERENCE are used to indicate how the data is passed. If none of these keywords are used, the parameter is passed by reference by default. When passing by value, the modified content of the parameter is assigned to the actual parameter only if the method is completed without errors. This is illustrated in the following example:

REPORT zre_test_pass_by_changing.

DATA gv_time_by_reference_implicit TYPE t.

DATA gv_time_by_reference_explicit TYPE t.

DATA gv_time_by_value TYPE t.

CLASS lcl_time_utility DEFINITION FINAL.

  PUBLIC SECTION.

    CLASS-METHODS add_10_seconds_successfully

      CHANGING

        cv_time_by_reference_implicit            TYPE t

        REFERENCE(cv_time_by_reference_explicit) TYPE t

        VALUE(cv_time_by_value)                  TYPE t.

    CLASS-METHODS add_10_seconds_with_exception

      CHANGING

        cv_time_by_reference_implicit            TYPE t

        REFERENCE(cv_time_by_reference_explicit) TYPE t

        VALUE(cv_time_by_value)                  TYPE t

      RAISING

        cx_abap_datfm_invalid_date.

  PROTECTED SECTION.

  PRIVATE SECTION.

ENDCLASS.

CLASS lcl_time_utility IMPLEMENTATION.

  METHOD add_10_seconds_successfully.

    cv_time_by_reference_implicit = cv_time_by_reference_implicit + 10.

    cv_time_by_reference_explicit = cv_time_by_reference_explicit + 10.

    cv_time_by_value = cv_time_by_value + 10.

  ENDMETHOD.

  METHOD add_10_seconds_with_exception.

    cv_time_by_reference_implicit = cv_time_by_reference_implicit + 10.

    cv_time_by_reference_explicit = cv_time_by_reference_explicit + 10.

    cv_time_by_value = cv_time_by_value + 10.

    RAISE EXCEPTION TYPE cx_abap_datfm_invalid_date.

  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.

  gv_time_by_reference_implicit = sy-uzeit.

  gv_time_by_reference_explicit = sy-uzeit.

  gv_time_by_value = sy-uzeit.

  WRITE: / `Start time:`.

  WRITE: / gv_time_by_value.

  WRITE: / `Method call successful:`.

  lcl_time_utility=>add_10_seconds_successfully(

    CHANGING

      cv_time_by_reference_implicit = gv_time_by_reference_implicit

      cv_time_by_reference_explicit = gv_time_by_reference_explicit

      cv_time_by_value              = gv_time_by_value ).

  WRITE: / `Pass by reference (implicit):`.

  WRITE: / gv_time_by_reference_implicit.

  WRITE: / `Pass by reference (explicit):`.

  WRITE: / gv_time_by_reference_explicit.

  WRITE: / `Pass by value:`.

  WRITE: / gv_time_by_value.

  WRITE: / `Method call unsuccessful:`.

  TRY.

      lcl_time_utility=>add_10_seconds_with_exception(

        CHANGING

          cv_time_by_reference_implicit = gv_time_by_reference_implicit

          cv_time_by_reference_explicit = gv_time_by_reference_explicit

          cv_time_by_value              = gv_time_by_value ).

    CATCH cx_abap_datfm_invalid_date.

      " do nothing

  ENDTRY.

  WRITE: / `Pass by reference (implicit):`.

  WRITE: / gv_time_by_reference_implicit.

  WRITE: / `Pass by reference (explicit):`.

  WRITE: / gv_time_by_reference_explicit.

  WRITE: / `Pass by value:`.

  WRITE: / gv_time_by_value.

The report generates the following output to the screen:

Start time:

142101

Method call successful:

Pass by reference (implicit):

142111

Pass by reference (explicit):

142111

Pass by value:

142111

Method call unsuccessful:

Pass by reference (implicit):

142121

Pass by reference (explicit):

142121

Pass by value:

142111

The RETURNING scenario

In the RETURNING section of the method signature, the keyword VALUE must be used since pass by value is the only option. The modified content of the parameter is assigned to the actual parameter only if the method is completed without errors. This is illustrated in the following example:

REPORT zre_test_pass_by_returning.

DATA gv_time_by_value TYPE t.

CLASS lcl_time_utility DEFINITION FINAL.

  PUBLIC SECTION.

    CLASS-METHODS do_nothing

      RETURNING

        VALUE(result) TYPE t.

    CLASS-METHODS get_time_120000_successfully

      RETURNING

        VALUE(result) TYPE t.

    CLASS-METHODS get_time_120000_with_exception

      RETURNING

        VALUE(result) TYPE t

      RAISING

        cx_abap_datfm_invalid_date.

  PROTECTED SECTION.

  PRIVATE SECTION.

ENDCLASS.

CLASS lcl_time_utility IMPLEMENTATION.

  METHOD do_nothing.

  ENDMETHOD.

  METHOD get_time_120000_successfully.

    result = '120000'.

  ENDMETHOD.

  METHOD get_time_120000_with_exception.

    result = '120000'.

    RAISE EXCEPTION TYPE cx_abap_datfm_invalid_date.

  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.

  gv_time_by_value = sy-uzeit.

  WRITE: / `Start time:`.

  WRITE: / gv_time_by_value.

  WRITE: / `Do nothing:`.

  gv_time_by_value = lcl_time_utility=>do_nothing( ).

  WRITE: / `Pass by value:`.

  WRITE: / gv_time_by_value.

  WRITE: / `Method call successful:`.

  CLEAR gv_time_by_value.

  gv_time_by_value = lcl_time_utility=>get_time_120000_successfully( ).

  WRITE: / `Pass by value:`.

  WRITE: / gv_time_by_value.

  WRITE: / `Method call unsuccessful:`.

  CLEAR gv_time_by_value.

  TRY.

      gv_time_by_value = lcl_time_utility=>get_time_120000_with_exception( ).

    CATCH cx_abap_datfm_invalid_date.

      " do nothing

  ENDTRY.

  WRITE: / `Pass by value:`.

  WRITE: / gv_time_by_value.

The report generates the following output to the screen:

Start time:

145858

Do nothing:

Pass by value:

000000

Method call successful:

Pass by value:

120000

Method call unsuccessful:

Pass by value:

000000

The escape character !

The escape character ! can be used in the method signature to distinguish a parameter name from an ABAP word with the same name. The escape character itself isn’t part of the name. Example:

REPORT zre_test_escape.

DATA gv_time TYPE t.

CLASS lcl_time_utility DEFINITION FINAL.

  PUBLIC SECTION.

    CLASS-METHODS display_time

      IMPORTING

        !importing TYPE t.

  PROTECTED SECTION.

  PRIVATE SECTION.

ENDCLASS.

CLASS lcl_time_utility IMPLEMENTATION.

  METHOD display_time.

    WRITE: / `Pass by reference (implicit):`.

    WRITE: / importing.

    WAIT UP TO 2 SECONDS.

    gv_time = sy-uzeit.

    WRITE: / `Pass by reference (implicit) after 2 seconds wait:`.

    WRITE: / importing.

  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.

  gv_time = sy-uzeit.

  lcl_time_utility=>display_time( gv_time ).

The report generates the following output to the screen:

Pass by reference (implicit):

154728

Pass by reference (implicit) after 2 seconds wait:

154730

I can’t think of any example where I’ve felt the need to use the escape character, and it is probably a good idea to avoid it whenever possible.

No comments:

Post a Comment