Inline-Deklarationen: Kompakter und lesbarer ABAP-Code

kategorie
ABAP
Veröffentlicht
autor
Johannes

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.

SyntaxBeschreibungVerfügbar seit
DATA(var)Variable mit TypableitungABAP 7.40
FIELD-SYMBOL(<fs>)Feldsymbol mit TypableitungABAP 7.40
FINAL(var)Unveränderliche VariableABAP 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 -> string
DATA(lv_string) = |ABC|. " Typ: string
" Numerische Literale
DATA(lv_int) = 100. " Typ: i
DATA(lv_float) = '1.5'. " Typ: string (NICHT p oder f!)
" Explizite Konvertierung für korrekte Typen
DATA(lv_amount) = CONV netwr( '1234.56' ). " Typ: netwr
DATA(lv_quantity) = CONV menge( 10 ). " Typ: menge

FIELD-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

SituationEmpfehlung
LOOP, READ TABLE, SELECTImmer inline verwenden
MethodenrückgabenInline wenn Typ klar
Komplexe TypenExplizite Deklaration bevorzugen
Wiederverwendete VariablenExplizite 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ährungsfeld
DATA 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

Weiterführende Artikel