Pass by value or pass by reference? SAP ABAP.
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
(orEXIT
orCHECK
).
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.