Skip to Content
Technical Articles
Author's profile photo Lena Padeken

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.

Assigned Tags

      13 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo First Name Last Name
      First Name Last Name

      Even after reading through your examples I don't really see a use case for it. Your examples show how to to do it, but do not give any benefit of doing so.

      (I personally did quite some programming in Clojure, which makes a big fuzz about the immutability of it variables, but at the same time introduces ways of circumventing it). 

      Author's profile photo Horst Keller
      Horst Keller

      I think, we all know the benefit of constants.

      Well, here we have a kind of constants that can be filled by expressions.

      Author's profile photo First Name Last Name
      First Name Last Name

      Well, I think variables are called variables because they are variable?

      The funny thing for me is, that people use object orientation, and all class variables are actually a state vector of an object. Now they doing a big hype about states and immutability because they want to avoid statefulness in their programs.

      When you want to avoid statefulness you should not use object orientation at first place, but try to use a functional programming style (which by the way can be achieved by using forms or function module while at the same time not using global variables).

       

      Author's profile photo Suhas Saha
      Suhas Saha

      Your examples show how to to do it, but do not give any benefit of doing so.

      Have you ever worried about accidentally changing a field when using a field-symbol assigning (or a data reference) an internal table-line? If yes, then with immutable field-symbols you need not worry anymore.

      Tbh, i have been using FINAL for majority of my data declarations and rarely find the need to use DATA. Although, sometimes ADT quick fix refactoring options don't like FINAL 🙄

      Author's profile photo Horst Keller
      Horst Keller

      We replaced DATA(...) with FINAL( ) in the dcoumentation examples wherever possible ...

      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali

      Hello Lena,

      thanks for compiling this information.

      FINAL is decribed only as an inline declaration. It seems to me there should not be any problem with allowing FINAL to be used just like the DATA keyword for normal (non inline) data declaration.

      This means the creation of an immutable variable cannot be separated from its initialization. But I can imagine a use case with multiple initialization subroutines for an immutable variable where we guarantee the value declared as FINAL and passed as a changing parameter is initialized only once.

      Is there a rationale for not allowing this?

      best regards,

      Jacques Nomssi Nzali

      Author's profile photo Horst Keller
      Horst Keller

      Isn't CONSTANTS already what you want?

      And if you need an immutable variable for the context of a method, you can use

      METHOD ...
        FINAL(...) = ...
        ...
      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali

      Hello Horst,

      what I want is to be able to rewrite

        FINAL(org_unit) = get_organization( user ).

      as

        FINAL org_unit TYPE org_data.
        org_unit = get_organization( user ).

      or

        FINAL org_unit TYPE org_data.
        get_organization( EXPORTING user = user
                          CHANGING unit = org_unit ).

      I could extend this to a use case with multiple initialization steps. Generally speaking, we would have an identifier that is variable before the first assignment anywhere in code, than a immutable value after the first assignment.

      As I write this, I realize we would probably need an operator/predicate to test if a value was already assigned to a FINAL identifier, like IS ASSIGNED for field-symbols.

      So I recognize the functional call form that abstracts the initialization step will be good enough for me most of the time. So I would rewrite the LAST_CHANGE method in the blog to

        METHOD last_change.
          FINAL(time) = format_time( sy-timlo ).
          cl_demo_output=>write( |Last change: { sy-datlo } { time }| ).
        ENDMETHOD.

       

      best regards,

      JNN

       

       

      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali

      Hello Lena,

      can a "dirty" assign (like in the FUN_FACT method) be used to change the value of a variable declared with FINAL? (that would be consistent with the nickname).

      best regards,

      JNN

      Author's profile photo Lena Padeken
      Lena Padeken
      Blog Post Author

      Hello JNN,

      In the case of the assign example, the value of FINAL is set after the ASSIGN statement. If you try to overwrite the value with a subsequent ASSIGN, the uncatchable exception MOVE_TO_LIT_NOTALLOWED_NODATA will be raised:

      ASSIGN ('NUMBER1') TO FIELD-SYMBOL(<fs3>).
      <fs3> = 333.

      Best regards,

      Lena

      Author's profile photo Sascha Weidlich
      Sascha Weidlich

      Hi Lena Padeken ,

      first im glad that this Operator finally (:D) comes to ABAP. Just one Question, and that's a pretty big one:

      Why is the FINAL Operator allowed inside a LOOP Statement?

      For me if a FINAL Value is declared once it should never change. But when you make use of FINAL inside a LOOP it can change?! Where is the logical approach behind this?

      I build a Sandbox Program to play around with it and wrote something like

      DATA tab TYPE STANDARD Table of i.
      tab = VALUE #( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ).
      
      FINAL(date_value) = sy-datlo.
      
      LOOP AT tab ASSIGNING FIELD-SYMBOL(<number>).
        FINAL(number_value) = <number>.
      ENDLOOP.
      
      cl_demo_output=>write( number_value ).

      In this example I thought I receive an Syntax Error because "number_value" is initialized with 1, then it is 2.. After the LOOP it's 4.

       

      This is a very error prone implementation of a "immutable variable" in my opinion.  Could you share some insides on this decision?

      Best regards,

      Sascha

      Author's profile photo Lena Padeken
      Lena Padeken
      Blog Post Author

      Hi Sascha,

      A variable with the declaration operator FINAL is defined at exactly one write position, for example, inside a LOOP statement. At this position, a value can be assigned multiple times. As soon as you try to change the value of a variable declared with FINAL at another write position, this will lead to your expected syntax error.

      Best regards,

      Lena

      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali

      Hello Lena,

      I like your concise explanation of the FINAL declaration operator. I understand how this is suitable in the ABAP context. Note the term immutable is not used.

      The confusion IMHO stems from the use of the term immutable variable both in your blog and in the documentation. From this definition of immutable I would have expected the write protection to be independent of the write position. I understand this expectation is incorrect.

      Maybe we should just avoid using the term immutable. From now on I will say a FINAL declaration creates and/or updates a write position protected variable.

      Best regards,

      Jacques Nomssi Nzali