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 (
lenapadeken) for similar blog posts.