Inline-Deklarationen sind seit ABAP 7.40 verfügbar und revolutionieren die Art, wie wir Variablen und Feldsymbole deklarieren. Statt separate DATA- und FIELD-SYMBOLS-Statements zu schreiben, kannst du Variablen direkt dort deklarieren, wo du sie brauchst.
Was sind Inline-Deklarationen?
Inline-Deklarationen ermöglichen die Deklaration von Variablen und Feldsymbolen direkt in der Anweisung, die ihnen einen Wert zuweist. Der Compiler leitet den Typ automatisch ab.
| Syntax | Beschreibung | Verfügbar seit |
|---|---|---|
DATA(var) | Variable mit Typableitung | ABAP 7.40 |
FIELD-SYMBOL(<fs>) | Feldsymbol mit Typableitung | ABAP 7.40 |
FINAL(var) | Unveränderliche Variable | ABAP 7.57 |
Vorteile von Inline-Deklarationen
- Weniger Codezeilen: Keine separaten DATA-Statements nötig
- Automatische Typisierung: Der Compiler kennt den richtigen Typ
- Bessere Lesbarkeit: Deklaration und Verwendung am selben Ort
- Reduzierte Fehlerquellen: Keine Typ-Mismatches zwischen Deklaration und Verwendung
DATA() Inline-Deklaration
Grundlagen
Die einfachste Form der Inline-Deklaration verwendet 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. " Klassische Deklaration (alt) DATA lv_text_old TYPE string. lv_text_old = 'Hallo Welt'.
" Inline-Deklaration (neu) DATA(lv_text_new) = 'Hallo Welt'.
" Typ wird automatisch abgeleitet DATA(lv_number) = 42. " Typ: i (integer) DATA(lv_decimal) = '3.14'. " Typ: string (Vorsicht!) DATA(lv_packed) = CONV decfloat34( '3.14' ). " Typ: decfloat34
out->write( |Text: { lv_text_new }| ). out->write( |Number: { lv_number }| ). out->write( |Decimal: { lv_packed }| ).
" Methodenrückgabe direkt in Variable DATA(lv_timestamp) = cl_abap_context_info=>get_system_date( ). out->write( |Datum: { lv_timestamp }| ). ENDMETHOD.
ENDCLASS.Typableitung verstehen
Der Compiler leitet den Typ aus dem rechten Ausdruck ab:
" String-Literale -> csequence (char-ähnlich)DATA(lv_char) = 'ABC'. " Typ: char3
" String Template -> stringDATA(lv_string) = |ABC|. " Typ: string
" Numerische LiteraleDATA(lv_int) = 100. " Typ: iDATA(lv_float) = '1.5'. " Typ: string (NICHT p oder f!)
" Explizite Konvertierung für korrekte TypenDATA(lv_amount) = CONV netwr( '1234.56' ). " Typ: netwrDATA(lv_quantity) = CONV menge( 10 ). " Typ: mengeFIELD-SYMBOL() Inline-Deklaration
Feldsymbole können ebenfalls inline deklariert werden:
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. " Interne Tabelle erstellen 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 ) ).
" Klassische LOOP mit Feldsymbol (alt) " FIELD-SYMBOLS <fs_flight_old> TYPE ty_s_flight. " LOOP AT lt_flights ASSIGNING <fs_flight_old>. " ENDLOOP.
" Inline-Deklaration im LOOP (neu) LOOP AT lt_flights ASSIGNING FIELD-SYMBOL(<fs_flight>). " Direkter Schreibzugriff möglich <fs_flight>-price = <fs_flight>-price * '1.1'. out->write( |{ <fs_flight>-carrid } { <fs_flight>-connid }: { <fs_flight>-price }| ). ENDLOOP.
" Inline FIELD-SYMBOL bei READ TABLE READ TABLE lt_flights ASSIGNING FIELD-SYMBOL(<fs_found>) WITH KEY carrid = 'AA'. IF sy-subrc = 0. out->write( |Gefunden: { <fs_found>-carrid } { <fs_found>-connid }| ). ENDIF. ENDMETHOD.
ENDCLASS.Inline-Deklaration in LOOP
Die häufigste Verwendung von Inline-Deklarationen ist in LOOP-Konstrukten:
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 mit INTO für Kopie (lesend) LOOP AT lt_orders INTO DATA(ls_order). out->write( |Order für Kunde { ls_order-customer_id }: { ls_order-amount }| ). ENDLOOP.
" LOOP mit ASSIGNING für Referenz (schreibend) LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<fs_order>) WHERE status = 'N'. <fs_order>-status = 'P'. " Direktes Update out->write( |Status geändert für Betrag { <fs_order>-amount }| ). ENDLOOP.
" LOOP mit INDEX für Zeilennummer LOOP AT lt_orders INTO DATA(ls_ord) INDEX INTO DATA(lv_index). out->write( |Zeile { lv_index }: { ls_ord-amount }| ). ENDLOOP.
" LOOP AT GROUP BY für Gruppierung 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( |Kunde { lt_group-customer_id } Gesamt: { lv_total }| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Inline-Deklaration in READ TABLE
READ TABLE profitiert besonders von Inline-Deklarationen:
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 mit INTO und Inline-Deklaration READ TABLE lt_products INTO DATA(ls_product) WITH TABLE KEY product_id = 'PROD001'. IF sy-subrc = 0. out->write( |Produkt: { ls_product-name } - { ls_product-price } EUR| ). ENDIF.
" READ TABLE mit ASSIGNING und Inline-Deklaration 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. " Stock reduzieren out->write( |Neuer Stock für { <fs_product>-name }: { <fs_product>-stock }| ). ENDIF.
" READ TABLE mit REFERENCE INTO READ TABLE lt_products REFERENCE INTO DATA(lr_product) WITH TABLE KEY product_id = 'PROD003'. IF sy-subrc = 0. out->write( |Referenz: { lr_product->name }| ). ENDIF.
" Tabellenausdruck mit DEFAULT (ohne sy-subrc) DATA(ls_found) = VALUE #( lt_products[ product_id = 'PROD001' ] DEFAULT VALUE #( name = 'Nicht gefunden' ) ). out->write( |Gefunden: { ls_found-name }| ).
" Optional: Line Exists prüfen IF line_exists( lt_products[ product_id = 'PROD999' ] ). out->write( 'Produkt existiert' ). ELSE. out->write( 'Produkt nicht gefunden' ). ENDIF. ENDMETHOD.
ENDCLASS.Inline-Deklaration in SELECT
Inline-Deklarationen in SELECT-Statements machen den Code besonders kompakt:
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 in interne Tabelle mit Inline-Deklaration SELECT carrid, connid, fldate, price FROM sflight WHERE carrid = 'LH' INTO TABLE @DATA(lt_flights).
IF sy-subrc = 0. out->write( |{ lines( lt_flights ) } Flüge gefunden| ). ENDIF.
" SELECT SINGLE mit Inline-Deklaration 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 mit LOOP und Inline-Deklaration SELECT carrid, carrname FROM scarr INTO @DATA(ls_carrier). out->write( |Airline: { ls_carrier-carrid } - { ls_carrier-carrname }| ). ENDSELECT.
" Aggregation mit Inline-Deklaration 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() für unveränderliche Variablen
Seit ABAP 7.57 gibt es FINAL() für Konstanten mit Typableitung:
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 für unveränderliche Werte FINAL(lc_max_items) = 100. FINAL(lc_api_endpoint) = |https://api.example.com/v1|.
" Kompilerfehler wenn man FINAL-Variable ändern will: " lc_max_items = 200. " <- Dies würde einen Fehler erzeugen!
out->write( |Max Items: { lc_max_items }| ). out->write( |API: { lc_api_endpoint }| ).
" FINAL in LOOP (jede Iteration ist neu, daher erlaubt) DATA(lt_values) = VALUE string_table( ( `A` ) ( `B` ) ( `C` ) ).
LOOP AT lt_values INTO FINAL(lv_value). out->write( |Wert: { lv_value }| ). " lv_value = 'X'. " <- Fehler: FINAL-Variable nicht änderbar ENDLOOP.
" FINAL bei Methodenrückgaben FINAL(lv_today) = cl_abap_context_info=>get_system_date( ). out->write( |Heute: { lv_today }| ). ENDMETHOD.
ENDCLASS.Best Practices und Lesbarkeit
Wann Inline-Deklarationen verwenden
| Situation | Empfehlung |
|---|---|
| LOOP, READ TABLE, SELECT | Immer inline verwenden |
| Methodenrückgaben | Inline wenn Typ klar |
| Komplexe Typen | Explizite Deklaration bevorzugen |
| Wiederverwendete Variablen | Explizite Deklaration am Anfang |
Anti-Patterns vermeiden
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. " SCHLECHT: Mehrfache Deklaration im gleichen Scope " DATA(lv_result) = calculate_a( ). " DATA(lv_result) = calculate_b( ). " Kompilerfehler!
" GUT: Eine Variable, mehrfache Zuweisung DATA lv_result TYPE i. lv_result = 10. lv_result = 20.
" SCHLECHT: Unklarer Typ durch Literals DATA(lv_unclear) = '123'. " Ist das ein String oder eine Zahl?
" GUT: Explizite Konvertierung DATA(lv_number) = CONV i( '123' ).
" SCHLECHT: Zu lange Zeilen durch Inline " DATA(ls_very_long) = VALUE ty_s_complex( field1 = 'a' field2 = 'b' ... ).
" GUT: Aufteilen bei komplexen Strukturen 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.Typableitung vs. explizite Typen
" Typableitung ist ideal für:" - Lokale, kurzlebige Variablen" - Methodenrückgabewerte" - LOOP/READ TABLE Ergebnisse
" Explizite Typen sind besser für:" - Schnittstellen-Parameter" - Klassenmember-Variablen" - Wenn der Typ dokumentiert werden soll" - Bei ABAP-typischen Währungs/Mengen-Feldern
" Beispiel: Expliziter Typ für WährungsfeldDATA lv_amount TYPE netwr.lv_amount = '1234.56'.
" vs. Inline (Typ muss trotzdem stimmen)DATA(lv_amount_inline) = CONV netwr( '1234.56' ).Fazit
Inline-Deklarationen machen ABAP-Code kompakter und lesbarer. Die wichtigsten Punkte:
- DATA() für normale Variablen mit Typableitung
- FIELD-SYMBOL() für Feldsymbole in LOOP und READ TABLE
- FINAL() für unveränderliche Werte (ab 7.57)
- Nutze CONV für explizite Typkonvertierung bei Literalen
- Verwende Inline-Deklarationen in LOOP, READ TABLE und SELECT
- Bei komplexen oder wiederverwendeten Variablen: klassische Deklaration am Methodenanfang