Inline declarations have been available since ABAP 7.40 and revolutionize how we declare variables and field symbols. Instead of writing separate DATA and FIELD-SYMBOLS statements, you can declare variables directly where you need them.
What Are Inline Declarations?
Inline declarations enable declaring variables and field symbols directly in the statement that assigns a value to them. The compiler automatically derives the type.
| Syntax | Description | Available Since |
|---|---|---|
DATA(var) | Variable with type inference | ABAP 7.40 |
FIELD-SYMBOL(<fs>) | Field symbol with type inference | ABAP 7.40 |
FINAL(var) | Immutable variable | ABAP 7.57 |
Advantages of Inline Declarations
- Fewer lines of code: No separate DATA statements needed
- Automatic typing: The compiler knows the correct type
- Better readability: Declaration and usage at the same location
- Reduced error sources: No type mismatches between declaration and usage
DATA() Inline Declaration
Basics
The simplest form of inline declaration uses DATA():
CLASS zcl_inline_basics DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_inline_basics IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Classic declaration (old) DATA lv_text_old TYPE string. lv_text_old = 'Hello World'.
" Inline declaration (new) DATA(lv_text_new) = 'Hello World'.
" Type is automatically inferred DATA(lv_number) = 42. " Type: i (integer) DATA(lv_decimal) = '3.14'. " Type: string (Caution!) DATA(lv_packed) = CONV decfloat34( '3.14' ). " Type: decfloat34
out->write( |Text: { lv_text_new }| ). out->write( |Number: { lv_number }| ). out->write( |Decimal: { lv_packed }| ).
" Method return directly into variable DATA(lv_timestamp) = cl_abap_context_info=>get_system_date( ). out->write( |Date: { lv_timestamp }| ). ENDMETHOD.
ENDCLASS.Understanding Type Inference
The compiler derives the type from the right-hand expression:
" String literals -> csequence (char-like)DATA(lv_char) = 'ABC'. " Type: char3
" String Template -> stringDATA(lv_string) = |ABC|. " Type: string
" Numeric literalsDATA(lv_int) = 100. " Type: iDATA(lv_float) = '1.5'. " Type: string (NOT p or f!)
" Explicit conversion for correct typesDATA(lv_amount) = CONV netwr( '1234.56' ). " Type: netwrDATA(lv_quantity) = CONV menge( 10 ). " Type: mengeFIELD-SYMBOL() Inline Declaration
Field symbols can also be declared inline:
CLASS zcl_inline_fieldsymbols DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_inline_fieldsymbols IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Create internal table DATA(lt_flights) = VALUE ty_t_flight( ( carrid = 'LH' connid = '400' price = 500 ) ( carrid = 'LH' connid = '401' price = 600 ) ( carrid = 'AA' connid = '100' price = 800 ) ).
" Classic LOOP with field symbol (old) " FIELD-SYMBOLS <fs_flight_old> TYPE ty_s_flight. " LOOP AT lt_flights ASSIGNING <fs_flight_old>. " ENDLOOP.
" Inline declaration in LOOP (new) LOOP AT lt_flights ASSIGNING FIELD-SYMBOL(<fs_flight>). " Direct write access possible <fs_flight>-price = <fs_flight>-price * '1.1'. out->write( |{ <fs_flight>-carrid } { <fs_flight>-connid }: { <fs_flight>-price }| ). ENDLOOP.
" Inline FIELD-SYMBOL with READ TABLE READ TABLE lt_flights ASSIGNING FIELD-SYMBOL(<fs_found>) WITH KEY carrid = 'AA'. IF sy-subrc = 0. out->write( |Found: { <fs_found>-carrid } { <fs_found>-connid }| ). ENDIF. ENDMETHOD.
ENDCLASS.Inline Declaration in LOOP
The most common use of inline declarations is in LOOP constructs:
CLASS zcl_inline_loop DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_order, order_id TYPE sysuuid_x16, customer_id TYPE i, amount TYPE p DECIMALS 2, status TYPE c LENGTH 1, END OF ty_s_order. TYPES ty_t_orders TYPE STANDARD TABLE OF ty_s_order WITH EMPTY KEY.
ENDCLASS.
CLASS zcl_inline_loop IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. DATA(lt_orders) = VALUE ty_t_orders( ( customer_id = 1 amount = '100.00' status = 'N' ) ( customer_id = 1 amount = '250.00' status = 'P' ) ( customer_id = 2 amount = '175.50' status = 'C' ) ).
" LOOP with INTO for copy (read-only) LOOP AT lt_orders INTO DATA(ls_order). out->write( |Order for customer { ls_order-customer_id }: { ls_order-amount }| ). ENDLOOP.
" LOOP with ASSIGNING for reference (write access) LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<fs_order>) WHERE status = 'N'. <fs_order>-status = 'P'. " Direct update out->write( |Status changed for amount { <fs_order>-amount }| ). ENDLOOP.
" LOOP with INDEX for row number LOOP AT lt_orders INTO DATA(ls_ord) INDEX INTO DATA(lv_index). out->write( |Row { lv_index }: { ls_ord-amount }| ). ENDLOOP.
" LOOP AT GROUP BY for grouping LOOP AT lt_orders INTO DATA(ls_customer_order) GROUP BY ( customer_id = ls_customer_order-customer_id ) INTO DATA(lt_group).
DATA(lv_total) = REDUCE p DECIMALS 2( INIT sum = CONV p DECIMALS 2( 0 ) FOR <order> IN GROUP lt_group NEXT sum = sum + <order>-amount ).
out->write( |Customer { lt_group-customer_id } Total: { lv_total }| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Inline Declaration in READ TABLE
READ TABLE benefits particularly from inline declarations:
CLASS zcl_inline_read_table DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_product, product_id TYPE c LENGTH 10, name TYPE string, price TYPE p DECIMALS 2, stock TYPE i, END OF ty_s_product. TYPES ty_t_products TYPE SORTED TABLE OF ty_s_product WITH UNIQUE KEY product_id.
ENDCLASS.
CLASS zcl_inline_read_table IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. DATA(lt_products) = VALUE ty_t_products( ( product_id = 'PROD001' name = 'Laptop' price = '999.99' stock = 50 ) ( product_id = 'PROD002' name = 'Mouse' price = '29.99' stock = 200 ) ( product_id = 'PROD003' name = 'Keyboard' price = '79.99' stock = 100 ) ).
" READ TABLE with INTO and inline declaration READ TABLE lt_products INTO DATA(ls_product) WITH TABLE KEY product_id = 'PROD001'. IF sy-subrc = 0. out->write( |Product: { ls_product-name } - { ls_product-price } EUR| ). ENDIF.
" READ TABLE with ASSIGNING and inline declaration READ TABLE lt_products ASSIGNING FIELD-SYMBOL(<fs_product>) WITH TABLE KEY product_id = 'PROD002'. IF sy-subrc = 0. <fs_product>-stock = <fs_product>-stock - 1. " Reduce stock out->write( |New stock for { <fs_product>-name }: { <fs_product>-stock }| ). ENDIF.
" READ TABLE with REFERENCE INTO READ TABLE lt_products REFERENCE INTO DATA(lr_product) WITH TABLE KEY product_id = 'PROD003'. IF sy-subrc = 0. out->write( |Reference: { lr_product->name }| ). ENDIF.
" Table expression with DEFAULT (without sy-subrc) DATA(ls_found) = VALUE #( lt_products[ product_id = 'PROD001' ] DEFAULT VALUE #( name = 'Not found' ) ). out->write( |Found: { ls_found-name }| ).
" Optional: Check Line Exists IF line_exists( lt_products[ product_id = 'PROD999' ] ). out->write( 'Product exists' ). ELSE. out->write( 'Product not found' ). ENDIF. ENDMETHOD.
ENDCLASS.Inline Declaration in SELECT
Inline declarations in SELECT statements make code particularly compact:
CLASS zcl_inline_select DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_inline_select IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " SELECT into internal table with inline declaration SELECT carrid, connid, fldate, price FROM sflight WHERE carrid = 'LH' INTO TABLE @DATA(lt_flights).
IF sy-subrc = 0. out->write( |{ lines( lt_flights ) } flights found| ). ENDIF.
" SELECT SINGLE with inline declaration SELECT SINGLE carrid, connid, cityfrom, cityto FROM spfli WHERE carrid = 'LH' AND connid = '400' INTO @DATA(ls_connection).
IF sy-subrc = 0. out->write( |Route: { ls_connection-cityfrom } -> { ls_connection-cityto }| ). ENDIF.
" SELECT with LOOP and inline declaration SELECT carrid, carrname FROM scarr INTO @DATA(ls_carrier). out->write( |Airline: { ls_carrier-carrid } - { ls_carrier-carrname }| ). ENDSELECT.
" Aggregation with inline declaration SELECT carrid, SUM( price ) AS total_revenue FROM sflight GROUP BY carrid INTO TABLE @DATA(lt_revenue).
LOOP AT lt_revenue INTO DATA(ls_rev). out->write( |{ ls_rev-carrid }: { ls_rev-total_revenue }| ). ENDLOOP. ENDMETHOD.
ENDCLASS.FINAL() for Immutable Variables
Since ABAP 7.57, FINAL() is available for constants with type inference:
CLASS zcl_inline_final DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_inline_final IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " FINAL for immutable values FINAL(lc_max_items) = 100. FINAL(lc_api_endpoint) = |https://api.example.com/v1|.
" Compiler error if you try to change FINAL variable: " lc_max_items = 200. " <- This would cause an error!
out->write( |Max Items: { lc_max_items }| ). out->write( |API: { lc_api_endpoint }| ).
" FINAL in LOOP (each iteration is new, so allowed) DATA(lt_values) = VALUE string_table( ( `A` ) ( `B` ) ( `C` ) ).
LOOP AT lt_values INTO FINAL(lv_value). out->write( |Value: { lv_value }| ). " lv_value = 'X'. " <- Error: FINAL variable cannot be changed ENDLOOP.
" FINAL for method return values FINAL(lv_today) = cl_abap_context_info=>get_system_date( ). out->write( |Today: { lv_today }| ). ENDMETHOD.
ENDCLASS.Best Practices and Readability
When to Use Inline Declarations
| Situation | Recommendation |
|---|---|
| LOOP, READ TABLE, SELECT | Always use inline |
| Method return values | Inline when type is clear |
| Complex types | Prefer explicit declaration |
| Reused variables | Explicit declaration at the beginning |
Avoiding Anti-Patterns
CLASS zcl_inline_best_practices DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_inline_best_practices IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " BAD: Multiple declarations in the same scope " DATA(lv_result) = calculate_a( ). " DATA(lv_result) = calculate_b( ). " Compile error!
" GOOD: One variable, multiple assignments DATA lv_result TYPE i. lv_result = 10. lv_result = 20.
" BAD: Unclear type from literals DATA(lv_unclear) = '123'. " Is this a string or a number?
" GOOD: Explicit conversion DATA(lv_number) = CONV i( '123' ).
" BAD: Lines too long due to inline " DATA(ls_very_long) = VALUE ty_s_complex( field1 = 'a' field2 = 'b' ... ).
" GOOD: Split for complex structures DATA(ls_order) = VALUE ty_s_order( order_id = cl_system_uuid=>create_uuid_x16_static( ) customer_id = 1 amount = '100.00' status = 'N' ).
out->write( |Result: { lv_result }| ). out->write( |Number: { lv_number }| ). ENDMETHOD.
ENDCLASS.Type Inference vs. Explicit Types
" Type inference is ideal for:" - Local, short-lived variables" - Method return values" - LOOP/READ TABLE results
" Explicit types are better for:" - Interface parameters" - Class member variables" - When the type should be documented" - ABAP-typical currency/quantity fields
" Example: Explicit type for currency fieldDATA lv_amount TYPE netwr.lv_amount = '1234.56'.
" vs. Inline (type must still be correct)DATA(lv_amount_inline) = CONV netwr( '1234.56' ).Conclusion
Inline declarations make ABAP code more compact and readable. The key points:
- DATA() for normal variables with type inference
- FIELD-SYMBOL() for field symbols in LOOP and READ TABLE
- FINAL() for immutable values (from 7.57)
- Use CONV for explicit type conversion with literals
- Use inline declarations in LOOP, READ TABLE, and SELECT
- For complex or reused variables: classic declaration at method start
Further Reading
- Modern ABAP Syntax
- Clean ABAP
- ABAP SQL Window Functions