Les declarations inline sont disponibles depuis ABAP 7.40 et revolutionnent la facon dont nous declarons les variables et les symboles de champ. Au lieu d’ecrire des instructions DATA et FIELD-SYMBOLS separees, vous pouvez declarer des variables directement la ou vous en avez besoin.
Que sont les declarations inline ?
Les declarations inline permettent de declarer des variables et des symboles de champ directement dans l’instruction qui leur attribue une valeur. Le compilateur deduit automatiquement le type.
| Syntaxe | Description | Disponible depuis |
|---|---|---|
DATA(var) | Variable avec inference de type | ABAP 7.40 |
FIELD-SYMBOL(<fs>) | Symbole de champ avec inference de type | ABAP 7.40 |
FINAL(var) | Variable immuable | ABAP 7.57 |
Avantages des declarations inline
- Moins de lignes de code : Pas d’instructions DATA separees necessaires
- Typage automatique : Le compilateur connait le bon type
- Meilleure lisibilite : Declaration et utilisation au meme endroit
- Sources d’erreurs reduites : Pas d’incompatibilites de type entre declaration et utilisation
Declaration inline DATA()
Bases
La forme la plus simple de declaration inline utilise 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. " Declaration classique (ancienne) DATA lv_text_old TYPE string. lv_text_old = 'Bonjour le monde'.
" Declaration inline (nouvelle) DATA(lv_text_new) = 'Bonjour le monde'.
" Le type est deduit automatiquement DATA(lv_number) = 42. " Type : i (integer) DATA(lv_decimal) = '3.14'. " Type : string (Attention !) 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 }| ).
" Retour de methode directement dans une variable DATA(lv_timestamp) = cl_abap_context_info=>get_system_date( ). out->write( |Date : { lv_timestamp }| ). ENDMETHOD.
ENDCLASS.Comprendre l’inference de type
Le compilateur deduit le type a partir de l’expression de droite :
" Litteraux string -> csequence (type char)DATA(lv_char) = 'ABC'. " Type : char3
" String Template -> stringDATA(lv_string) = |ABC|. " Type : string
" Litteraux numeriquesDATA(lv_int) = 100. " Type : iDATA(lv_float) = '1.5'. " Type : string (PAS p ou f !)
" Conversion explicite pour les types correctsDATA(lv_amount) = CONV netwr( '1234.56' ). " Type : netwrDATA(lv_quantity) = CONV menge( 10 ). " Type : mengeDeclaration inline FIELD-SYMBOL()
Les symboles de champ peuvent egalement etre declares 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. " Creer une table interne 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 ) ).
" LOOP classique avec symbole de champ (ancien) " FIELD-SYMBOLS <fs_flight_old> TYPE ty_s_flight. " LOOP AT lt_flights ASSIGNING <fs_flight_old>. " ENDLOOP.
" Declaration inline dans le LOOP (nouveau) LOOP AT lt_flights ASSIGNING FIELD-SYMBOL(<fs_flight>). " Acces en ecriture direct possible <fs_flight>-price = <fs_flight>-price * '1.1'. out->write( |{ <fs_flight>-carrid } { <fs_flight>-connid }: { <fs_flight>-price }| ). ENDLOOP.
" FIELD-SYMBOL inline avec READ TABLE READ TABLE lt_flights ASSIGNING FIELD-SYMBOL(<fs_found>) WITH KEY carrid = 'AA'. IF sy-subrc = 0. out->write( |Trouve : { <fs_found>-carrid } { <fs_found>-connid }| ). ENDIF. ENDMETHOD.
ENDCLASS.Declaration inline dans LOOP
L’utilisation la plus courante des declarations inline est dans les constructions LOOP :
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 avec INTO pour copie (lecture) LOOP AT lt_orders INTO DATA(ls_order). out->write( |Commande pour client { ls_order-customer_id }: { ls_order-amount }| ). ENDLOOP.
" LOOP avec ASSIGNING pour reference (ecriture) LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<fs_order>) WHERE status = 'N'. <fs_order>-status = 'P'. " Mise a jour directe out->write( |Statut modifie pour montant { <fs_order>-amount }| ). ENDLOOP.
" LOOP avec INDEX pour le numero de ligne LOOP AT lt_orders INTO DATA(ls_ord) INDEX INTO DATA(lv_index). out->write( |Ligne { lv_index }: { ls_ord-amount }| ). ENDLOOP.
" LOOP AT GROUP BY pour le regroupement 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( |Client { lt_group-customer_id } Total : { lv_total }| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Declaration inline dans READ TABLE
READ TABLE beneficie particulierement des declarations inline :
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 = 'Souris' price = '29.99' stock = 200 ) ( product_id = 'PROD003' name = 'Clavier' price = '79.99' stock = 100 ) ).
" READ TABLE avec INTO et declaration inline READ TABLE lt_products INTO DATA(ls_product) WITH TABLE KEY product_id = 'PROD001'. IF sy-subrc = 0. out->write( |Produit : { ls_product-name } - { ls_product-price } EUR| ). ENDIF.
" READ TABLE avec ASSIGNING et declaration inline 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. " Reduire le stock out->write( |Nouveau stock pour { <fs_product>-name }: { <fs_product>-stock }| ). ENDIF.
" READ TABLE avec 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.
" Expression de table avec DEFAULT (sans sy-subrc) DATA(ls_found) = VALUE #( lt_products[ product_id = 'PROD001' ] DEFAULT VALUE #( name = 'Non trouve' ) ). out->write( |Trouve : { ls_found-name }| ).
" Optionnel : Verifier Line Exists IF line_exists( lt_products[ product_id = 'PROD999' ] ). out->write( 'Produit existe' ). ELSE. out->write( 'Produit non trouve' ). ENDIF. ENDMETHOD.
ENDCLASS.Declaration inline dans SELECT
Les declarations inline dans les instructions SELECT rendent le code particulierement 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 dans une table interne avec declaration inline SELECT carrid, connid, fldate, price FROM sflight WHERE carrid = 'LH" INTO TABLE @DATA(lt_flights).
IF sy-subrc = 0. out->write( |{ lines( lt_flights ) } vols trouves| ). ENDIF.
" SELECT SINGLE avec declaration inline 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 avec LOOP et declaration inline SELECT carrid, carrname FROM scarr INTO @DATA(ls_carrier). out->write( |Compagnie : { ls_carrier-carrid } - { ls_carrier-carrname }| ). ENDSELECT.
" Aggregation avec declaration inline 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() pour les variables immuables
Depuis ABAP 7.57, il existe FINAL() pour les constantes avec inference de type :
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 pour les valeurs immuables FINAL(lc_max_items) = 100. FINAL(lc_api_endpoint) = |https://api.example.com/v1|.
" Erreur de compilation si on essaie de modifier une variable FINAL : " lc_max_items = 200. " <- Cela genererait une erreur !
out->write( |Max Items : { lc_max_items }| ). out->write( |API : { lc_api_endpoint }| ).
" FINAL dans LOOP (chaque iteration est nouvelle, donc autorise) DATA(lt_values) = VALUE string_table( ( `A` ) ( `B` ) ( `C` ) ).
LOOP AT lt_values INTO FINAL(lv_value). out->write( |Valeur : { lv_value }| ). " lv_value = 'X'. " <- Erreur : variable FINAL non modifiable ENDLOOP.
" FINAL avec retours de methode FINAL(lv_today) = cl_abap_context_info=>get_system_date( ). out->write( |Aujourd'hui : { lv_today }| ). ENDMETHOD.
ENDCLASS.Bonnes pratiques et lisibilite
Quand utiliser les declarations inline
| Situation | Recommandation |
|---|---|
| LOOP, READ TABLE, SELECT | Toujours utiliser inline |
| Retours de methode | Inline si le type est clair |
| Types complexes | Preferer la declaration explicite |
| Variables reutilisees | Declaration explicite au debut |
Eviter les 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. " MAUVAIS : Declaration multiple dans le meme scope " DATA(lv_result) = calculate_a( ). " DATA(lv_result) = calculate_b( ). " Erreur de compilation !
" BON : Une variable, affectations multiples DATA lv_result TYPE i. lv_result = 10. lv_result = 20.
" MAUVAIS : Type pas clair avec des litteraux DATA(lv_unclear) = '123'. " Est-ce un string ou un nombre ?
" BON : Conversion explicite DATA(lv_number) = CONV i( '123' ).
" MAUVAIS : Lignes trop longues avec inline " DATA(ls_very_long) = VALUE ty_s_complex( field1 = 'a' field2 = 'b' ... ).
" BON : Diviser pour les structures complexes 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.Inference de type vs types explicites
" L'inference de type est ideale pour :" - Variables locales ephemeres" - Valeurs de retour de methode" - Resultats LOOP/READ TABLE
" Les types explicites sont meilleurs pour :" - Parametres d'interface" - Variables membres de classe" - Quand le type doit etre documente" - Champs de devise/quantite typiques ABAP
" Exemple : Type explicite pour champ deviseDATA lv_amount TYPE netwr.lv_amount = '1234.56'.
" vs. Inline (le type doit quand meme correspondre)DATA(lv_amount_inline) = CONV netwr( '1234.56' ).Conclusion
Les declarations inline rendent le code ABAP plus compact et lisible. Les points cles :
- DATA() pour les variables normales avec inference de type
- FIELD-SYMBOL() pour les symboles de champ dans LOOP et READ TABLE
- FINAL() pour les valeurs immuables (a partir de 7.57)
- Utilisez CONV pour la conversion de type explicite avec des litteraux
- Utilisez les declarations inline dans LOOP, READ TABLE et SELECT
- Pour les variables complexes ou reutilisees : declaration classique en debut de methode