Lesson 2 of 6
Inline declarations and string templates
11 min
Listen to this lesson
Inline declarations remove the block of DATA statements at the top of every classic method. You declare a variable where it first receives a value, and its type comes from that value. String templates replace CONCATENATE and WRITE ... TO for building text. Together they change how almost every line of ABAP looks, so this lesson covers them first, including the traps that catch classic developers.
Inline declarations
| Where | Classic | Modern |
|---|---|---|
| Assignment | DATA lv_deposit TYPE p LENGTH 15 DECIMALS 2.lv_deposit = lv_qty * lv_rate. | DATA(deposit) = quantity * rate. |
| Method result | DATA lo_calc TYPE REF TO zcl_cmc_core_deposit.CREATE OBJECT lo_calc. | DATA(calc) = NEW zcl_cmc_core_deposit( ). |
| Loop work area | DATA ls_agr LIKE LINE OF lt_agr.LOOP AT lt_agr INTO ls_agr. | LOOP AT agreements INTO DATA(agr). |
| Field symbol | FIELD-SYMBOLS <ls_agr> LIKE LINE OF lt_agr.LOOP AT lt_agr ASSIGNING <ls_agr>. | LOOP AT agreements ASSIGNING FIELD-SYMBOL(<agr>). |
| SQL target | SELECT ... INTO TABLE lt_agr. | SELECT ... INTO TABLE @DATA(agreements). |
| Read once, never change | Not possible | FINAL(customer) = agr-customer. |
Inline declarations at the common write positions. Code is shown with a line break where the classic form needs two statements.
FINAL( ) declares an immutable variable: it can be written at exactly one position, and any other write is a syntax error or a runtime error. SAP introduced it in ABAP release 7.89 (SAP BTP ABAP environment 2208), which corresponds to ABAP Platform 2022 on premise (7.57); check your release. Kenji's team uses FINAL by default and DATA only when the value really changes, which makes the variables that change stand out.
Typing traps
The type of an inline variable is derived from the right-hand side. That is precise, and it surprises people:
DATA(count) = 0.gives typei. A latercount = count + 0.5truncates silently.DATA(code) = 'X'.gives typeclength 1. Assigning'OPEN'later stores'O'.DATA(name) = `Great Lakes`.with backquotes gives typestring; with single quotes, a fixed-lengthcfield.DATA(deposit) = quantity * rate.takes the calculation type of the expression, which may not be the packed type your table field uses. When the type matters, useCONVor declare explicitly.
Inline declarations also work in method calls with EXPORTING/IMPORTING parameters: calc->get_rate( EXPORTING product = item-product IMPORTING rate = DATA(rate) ). For functional methods, prefer returning values, so the call itself is the right-hand side: DATA(rate) = calc->get_rate( item-product ). Clean ABAP recommends returning one value rather than many exporting parameters, which also makes the code easier to test: the test simply asserts the returned value.
Scope: inline is not local to the block
A variable declared inline in a LOOP or IF is visible in the whole method from the point of declaration, not only inside the block. Declaring DATA(agr) in two loops in the same method is a syntax error. The second loop simply reuses agr, or better, the method is split. Long methods with many inline variables are a sign that the method does too much.
String templates
A string template sits between vertical bars and embeds expressions in curly braces: |Deposit { deposit } USD for { customer }|. Inside the braces you can put any expression, including method calls, and format it with options.
| Need | Template | Result (Contoso data) |
|---|---|---|
| Simple text | |Agreement { agr_id } is { status }| | Agreement 100045 is OPEN |
| Strip leading zeros | |{ customer ALPHA = OUT }| | 17100001 (stored as 0017100001) |
| Add leading zeros | |{ customer ALPHA = IN }| | 0017100001 |
| User date format | |{ valid_to DATE = USER }| | Date in the user's format |
| Number format | |{ deposit NUMBER = USER }| | 540.00 in the user's decimal notation |
| Fixed width | |{ product WIDTH = 12 }| | CMC-ALT-100 padded to 12 characters |
| Concatenate | text = text && | { product }|. | Appends the product |
Common string template options. Check the formatting options in the keyword documentation for your release.
- FindCONCATENATE 'Deposit' lv_amount 'USD' INTO lv_text SEPARATED BY space
- Noticelv_amount is packed; CONCATENATE needs character-like operands, so the old code first moved it with WRITE ... TO
- Writetext = |Deposit { amount } USD|.
- CheckDecimal separator and format: add NUMBER = USER if users see the text
- TestUnit test asserts the exact text for 540.00
What goes wrong in testing
- Format drift.
WRITE ... TOapplied user formatting; a plain template does not. A text that showed540,00for a German-format user now shows540.00. Unit tests with fixed expected strings catch this. - Leading zeros. A refactored message prints
0017100001where the old code printed17100001. AddALPHA = OUT. - Truncation. A
DATA(code) = 'X'later assigned a longer value cuts it off without an error. - Shadowed names. Inline variables named like old globals (
lv_prefixes) make reviews harder; Clean ABAP recommends dropping Hungarian prefixes in new code.
What would you do?
Ana, the accounts payable clerk, reports that a refunded deposit message now shows the customer as 0017100001. Kenji had refactored the message from WRITE ... TO into a string template. What is the fix?
- Declare variables where they get their first value; use FINAL( ) when the value never changes.
- Inline types come from the right-hand side: watch literals and calculation types.
- Inline variables are visible in the whole method after their declaration.
- String templates replace CONCATENATE and WRITE ... TO; add ALPHA, DATE and NUMBER options where formatting matters.
Check your understanding · not graded
Lesson check
1. What type does DATA(count) = 0. give count?
2. What does FINAL( ) declare?
3. Which template prints customer 0017100001 as 17100001?
4. Kenji declares DATA(agr) in two loops in one method. What happens?
5. Why might a refactored text show 540.00 where users saw 540,00?