Finally a declaration of immutable variables with FINAL in ABAP

Yep, we already spilled the beans in our last blog which introduced the language element STEP for ABAP internal tables (SAP BTP ABAP Environment 2202). Nonetheless, if you haven’t heard of it yet… Here’s another new ABAP language element: It’s FINAL! Available with ABAP Release 7.89, SAP BTP ABAP Environment 2208.

Basics

While writing code in ABAP, we often would like to declare immutable variables instead of mutable variables to avoid mutable state, like overwritten results, any unwanted changes, or even security threads. The concept of immutable variables can also be referred to as static single assignment and has been wished for from the SAP Community here. Now, the keyword FINAL is introduced as the declaration operator of a declaration expression FINAL(var) to declare an immutable variable var. The inline declaration with FINAL (for immutable variables) works similar to the inline declaration with DATA (for mutable variables). Assigning a value to an immutable variable is possible at exactly one write position in the current context. The value cannot be changed at other write positions. If you try other positions for a write access of an immutable variable, either a syntax error (if detected by the compiler) or the uncatchable exception MOVE_TO_LIT_NOTALLOWED_NODATA (if detected at runtime) will be raised. Want a quick overview? Scroll down or click here.

ABAP Programming Guidelines

To get a feeling when to use FINAL and when not, the ABAP Programming Guidelines offer guidance for you. The rule for immutable variables states:

Whenever you want a variable to be filled at exactly one write position and to be read only at all other positions, use an immutable variable.

The ABAP Programming Guidelines also offer a good and bad example to point out the efficient use of the declaration operator FINAL. Since we’ve already covered the basics, let’s get straight to some examples and find out when to use an immutable variable.

Use Case

Connecting this use case to the one from the blog about the keyword STEP, we’re back to purchase orders. For the use case of the previous blog, we imagined to work with customer data and purchase order data as one part of our daily work. Preparing for the tasks concerning our work, we constructed an internal table which represented a joined table of customer and purchase data. For this use case, we’ll work with an internal table as well as with a database table. Even though the following examples might not necessarily be realistic, they emphasize the use of immutable variables in ABAP.

Quarterly purchase orders

Remember the quarterly orders of all customers? If not, no worries, this example is covered wholly. Below, you find the basic construct for the use case involving three methods representing the three examples.

CLASS purchase_orders DEFINITION. PUBLIC SECTION. TYPES: BEGIN OF customer_purchase, cust_id TYPE c LENGTH 8, cust_name TYPE string, item_id TYPE c LENGTH 9, purch_date TYPE datn, proc_date TYPE datn, END OF customer_purchase, customer_purchases TYPE STANDARD TABLE OF customer_purchase WITH EMPTY KEY. METHODS quarterly_purchase_order. METHODS customer_purchase_count. METHODS last_change.
ENDCLASS. CLASS purchase_orders IMPLEMENTATION. "...
ENDCLASS. START-OF-SELECTION. DATA(inquiry) = NEW purchase_orders( ). inquiry->quarterly_purchase_order( ). inquiry->customer_purchase_count( ). inquiry->last_change( ). cl_demo_output=>display( ).

For the first example, we get an inquiry to list all purchase orders made in the fourth quarter of the year 2021 sorted from newest date to oldest date. The result will be used for further processing, like determining customer benefits. The internal table is constructed in the method quarterly_purchase_order and the orders table is filled according to the inquiry.

CLASS purchase_orders IMPLEMENTATION. "... METHOD quarterly_purchase_order. TYPES: BEGIN OF order, item_id TYPE c LENGTH 9, purch_date TYPE datn, proc_date TYPE datn, END OF order. DATA orders TYPE TABLE OF order. DATA(customer_purchases) = VALUE customer_purchases( ( cust_id = '00000001' cust_name = `Customer A` item_id = '781029348' purch_date = `20211108` proc_date = `20211109` ) ( cust_id = '00000001' cust_name = `Customer A` item_id = '781028275' purch_date = `20211117` proc_date = `20211118` ) ( cust_id = '00000001' cust_name = `Customer A` item_id = '781029350' purch_date = `20211203` proc_date = `20211206` ) ( cust_id = '00000002' cust_name = `Customer B` item_id = '781029348' purch_date = `20211207` proc_date = `20211208` ) ( cust_id = '00000003' cust_name = `Customer C` item_id = '781029353' purch_date = `20211215` proc_date = `20211216` ) ( cust_id = '00000004' cust_name = `Customer D` item_id = '781029321' purch_date = `20211215` proc_date = `20211216` ) ( cust_id = '00000005' cust_name = `Customer E` item_id = '781029342' purch_date = `20211216` proc_date = `20211217` ) ). LOOP AT customer_purchases REFERENCE INTO FINAL(customer_purchase) STEP -1 WHERE purch_date <= `20211231` AND purch_date >= `20211001`. orders = VALUE #( BASE orders ( item_id = customer_purchase->item_id purch_date = customer_purchase->purch_date proc_date = customer_purchase->proc_date ) ). FINAL(quarter) = `Quarter 4 2021`. ENDLOOP. cl_demo_output=>write( |Orders of all customers in { quarter }:| ). cl_demo_output=>write( orders ). ENDMETHOD. "...
ENDCLASS.

In total, one inline declaration with DATA and two inline declarations with FINAL were applied. The reasons might be obvious:

  • DATA(customer_purchases): The declaration operator FINAL could be used too but we expect that values might be inserted later on. You could argue to declare immutable variables as long as you really need mutable variables; in this example, we can be sure that the values of the internal table will change and thus we save the time of changing the variable later on.
  • FINAL(customer_purchase): The first appearance of the declaration operator FINAL is inside a LOOP AT statement after INTO. Using FINAL inside a loop, a value is assigned to the variable var multiple times. This also shows that at runtime, multiple write accesses can be executed.
  • FINAL(quarter): The second appearance of the declaration operator FINAL is for declaring a text string literal. Usually, strings are expressed as immutable: In ABAP, both mutable and immutable declarations are possible.

The result of the quarterly purchase orders example shows all purchases orders in the period from October to December 2021 from the newest date to the oldest date.

Orders of all customers in Quarter 4 2021:

ITEM_ID PURCH_DATE PROC_DATE
781029342 2021-12-16 2021-12-17
781029321 2021-12-15 2021-12-16
781029353 2021-12-15 2021-12-16
781029348 2021-12-07 2021-12-08
781029350 2021-12-03 2021-12-06
781028275 2021-11-17 2021-11-18
781029348 2021-11-08 2021-11-09

Amount of customer orders

For the second example, we’ll switch to database access. The internal table, constructed in the former example, is now a database table (exemplified here as db_customer_purchases). The inquiry is to count all purchase orders made by each customer and to store the entries in an internal table.

CLASS purchase_orders IMPLEMENTATION. "... METHOD customer_purchase_count. SELECT cust_id, COUNT(*) AS count FROM db_customer_purchases GROUP BY cust_id ORDER BY cust_id INTO TABLE @FINAL(customer_count). cl_demo_output=>write( customer_count ). ENDMETHOD. "...
ENDCLASS.

This example demonstrates the use of the declaration operator FINAL when assigning the data of the result set to an internal table.

The result of the amount of customer orders example shows all customer IDs and the amount of purchase orders.

Customers and their amount of ordered items:

CUST_ID COUNT
00000001 3
00000002 1
00000003 1
00000004 1
00000005 1

Last update

For the third example, you want to know the time and date when you last assigned the result set of the query from the previous example to the internal table customer_count.

CLASS purchase_orders IMPLEMENTATION. "... METHOD last_change. FINAL(time) = COND string( LET t = '130000' IN WHEN sy-timlo < t AND sy-timlo > '010000' THEN |{ sy-timlo TIME = ISO } AM| WHEN sy-timlo > t AND sy-timlo < '010000' THEN |{ CONV t( sy-timlo - 12 * 3600 ) TIME = ISO } PM| WHEN sy-timlo = '120000' THEN `High Noon` ELSE `Error` ). cl_demo_output=>write( |Last change: { sy-datlo } { time }| ). ENDMETHOD.
ENDCLASS.

In this example, the inline declaration operator FINAL declares the variable time. The value of time is constructed with the conditional operator COND of a conditional expression. The example is based on an example of the ABAP Keyword Documentation. Using FINAL in this context emphasizes that expressions can be defined as values of immutable variables.

The result of the last update example shows the time according to ISO 8601 and the date when the result set of the query of example two was last assigned to the internal table customer_count.

Last change: 20220825 12:09:12 AM

Why is it FINAL and not CONSTANT, CONST, or IMMUTABLE?

There was already lots of discussion going on about the naming of the keyword FINAL in this blog. So, perhaps for some of you this might be the most anticipated section.

In the beginning of the discussion regarding the static single assignment and the naming of the inline declaration operator, CONST was favored over other names. Reasons being, for example, that there was already the statement CONSTANTS for declaring a constant data object or a constant and that there was already the addition FINAL for classes and methods. When getting to the details of the different names, there is, however, a difference between a data object declared as a constant and a variable declared as final:

  • Constant: Defined with the CONSTANTS declaration statement and only one start value can be assigned with the VALUE addition at compile time. When the program is executed, the value of a constant is stored unchangeable in the PXA.
  • Final: Defined at one write position, but multiple write accesses can be executed at runtime at this position. At runtime, the value of an immutable variable is not stored in the PXA.

Therefore, an immutable variable isn’t a constant but a write-protected variable that is handled similar to other write-protected variables, like input parameters passed by reference or key fields of internal tables.

Fun fact

Like all other data declarations in ABAP, both constants and immutable variables are statically visible behind their declaration only, but dynamically available in the whole program. Let’s look at an example:

METHOD fun_fact. "Fun fact (the infamous ABAP behavior) ASSIGN ('NUMBER1') TO FIELD-SYMBOL(<fs1>). ASSIGN ('NUMBER2') TO FIELD-SYMBOL(<fs2>). cl_demo_output=>new( )->write( <fs1> )->write( <fs2> )->display( ). FINAL(number1) = 111. CONSTANTS number2 TYPE i VALUE 222.
ENDMETHOD.

The example shows some infamous ABAP behavior. Right at the beginning, two fields are assigned dynamically to two field symbols. The dynamic assignment is possible even though these fields are only declared at the bottom of the example: number1 with the declaration operator FINAL and number2 with the statement CONSTANTS. What happens now is that both fields can be read (but not written) and accessed by using the field symbols before their declaration; the value of FINAL is set at the position of the FINAL operator, while the value of CONSTANTS is already stored in the PXA from the beginning. Thus, the result looks like the following:

0
222

Summary

The fundamental technical difference is that the values of constants are stored in the PXA and immutable variables are stored like other variables in the stack memory. Summarized, all differences are important for the naming of the inline declaration operator. The name CONST is out of discussion at this point for the reasons above and because it’s more similar to CONSTANTS than to FINAL and some programming languages use CONST as a type qualifier. Overall, the name of the inline declaration operator should be short and known. In Java, for example, FINAL is used as a modifier for immutable variables. This is why IMMUTABLE is also out of discussion. Considering the definitions and other programming languages, the naming of FINAL is the logical consequence. However, if you still have any doubts about the naming, the comment section is always open for you.

Quick Check

Looking at the list below, the most important take-aways for the declaration operator FINAL are listed.

  • The declaration operator FINAL can be used at the same positions as the declaration operator DATA with the only exception that the statement OPEN CURSOR cannot be used to declare an immutable cursor.
  • Immutable variables are write-protected variables that can be assigned a value at one write position only, but there for multiple times.
  • Immutable variables can make programs more robust and therefore, use FINAL as the first choice from now on.

The following two links refer to the ABAP Keyword Documentation:

Further information

You should now know how to use the new declaration operator FINAL in your projects. Use FINAL for declaring immutable variables. The examples given in this blog are intended for demonstration purposes only. Did you notice another new language element and are you excited to use the new declaration operator FINAL? Write your thoughts in the comment section. Don’t miss out on new language elements and follow my profile (Lena Padeken) for similar blog posts.