ABAP CONV et CAST : Conversion de types et casting

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

Les expressions constructeur CONV et CAST permettent des conversions de types inline. CONV convertit les types de données élémentaires, tandis que CAST est utilisé pour le casting de références d’objets.

CONV – Conversion de types

L’opérateur CONV convertit une valeur vers un autre type de données.

Syntaxe

CONV <type_cible>( <expression> )
CONV #( <expression> ) " Le type est déduit du contexte

Exemples pour CONV

1. Conversion de type basique

" String vers Integer
DATA(lv_int) = CONV i( '42' ).
WRITE: / lv_int. " 42
" Integer vers String
DATA(lv_str) = CONV string( 123 ).
WRITE: / lv_str. " 123
" Packed vers Float
DATA: lv_packed TYPE p DECIMALS 2 VALUE '123.45'.
DATA(lv_float) = CONV f( lv_packed ).
WRITE: / lv_float. " 1.2345E+02

2. Comparaison : CONV vs. conversion classique

" === CLASSIQUE ===
DATA: lv_string TYPE string VALUE '100',
lv_number TYPE i.
lv_number = lv_string. " Conversion implicite
" Ou avec MOVE
MOVE lv_string TO lv_number.
" === MODERNE AVEC CONV ===
DATA(lv_number2) = CONV i( '100' ).
" Inline dans les expressions
DATA(lv_result) = CONV i( '50' ) + CONV i( '30' ).
WRITE: / lv_result. " 80

3. CONV pour les nombres décimaux

" String vers Packed Decimal
DATA(lv_amount) = CONV p DECIMALS 2( '1234.56' ).
WRITE: / lv_amount. " 1234.56
" Division avec résultat correct
DATA: lv_a TYPE i VALUE 10,
lv_b TYPE i VALUE 3.
" Sans CONV : Division entière
DATA(lv_div1) = lv_a / lv_b.
WRITE: / lv_div1. " 3 (tronqué)
" Avec CONV : Résultat décimal
DATA(lv_div2) = CONV decfloat34( lv_a ) / lv_b.
WRITE: / lv_div2. " 3.333...

4. CONV dans les calculs

DATA: lv_quantity TYPE i VALUE 7,
lv_total TYPE i VALUE 100.
" Calcul de pourcentage avec résultat décimal
DATA(lv_percentage) = CONV decfloat16( lv_quantity * 100 / lv_total ).
WRITE: / lv_percentage, '%'. " 7%
" Calcul précis
DATA(lv_price) = CONV p DECIMALS 2( 100 / 3 ).
WRITE: / lv_price. " 33.33

5. CONV pour les chaînes de caractères

" Char vers String
DATA: lv_char TYPE c LENGTH 10 VALUE 'Test'.
DATA(lv_string) = CONV string( lv_char ).
" Numérique vers String formaté
DATA: lv_num TYPE i VALUE 42.
DATA(lv_formatted) = |Numéro : { CONV string( lv_num ) }|.
" NUMC vers Integer
DATA: lv_numc TYPE n LENGTH 10 VALUE '0000000123'.
DATA(lv_int_from_numc) = CONV i( lv_numc ).
WRITE: / lv_int_from_numc. " 123

6. CONV avec notation # (Déduction de type)

METHODS: process_amount
IMPORTING iv_amount TYPE p DECIMALS 2.
" Le type est déduit du paramètre
process_amount( iv_amount = CONV #( '99.99' ) ).
" Lors des affectations
DATA: lv_target TYPE p DECIMALS 2.
lv_target = CONV #( '123.45' ). " Type déduit de lv_target

7. CONV pour date et heure

" String vers Date
DATA(lv_date) = CONV d( '20241115' ).
WRITE: / lv_date. " 20241115
" Timestamp
DATA(lv_ts) = CONV timestamp( '20241115143000' ).
" Assembler une date à partir de parties
DATA: lv_year TYPE n LENGTH 4 VALUE '2024',
lv_month TYPE n LENGTH 2 VALUE '11',
lv_day TYPE n LENGTH 2 VALUE '15'.
DATA(lv_date2) = CONV d( |{ lv_year }{ lv_month }{ lv_day }| ).

8. CONV dans les appels de méthodes

METHODS: get_discount
IMPORTING iv_amount TYPE p DECIMALS 2
RETURNING VALUE(rv_discount) TYPE p DECIMALS 2.
" Conversion directe lors de l'appel
DATA(lv_discount) = get_discount( iv_amount = CONV #( 100 ) ).
" Avec plusieurs paramètres
calculate(
iv_quantity = CONV i( '5' )
iv_price = CONV p DECIMALS 2( '19.99' )
).

9. CONV avec Built-in Types

" Différents types
DATA(lv_i) = CONV i( '42' ). " Integer
DATA(lv_i8) = CONV int8( '9999999999' ). " Integer 8 octets
DATA(lv_f) = CONV f( '3.14159' ). " Floating Point
DATA(lv_p) = CONV p DECIMALS 4( '123.4567' ). " Packed
DATA(lv_df16) = CONV decfloat16( '123.456789' ). " Decimal Float 16
DATA(lv_df34) = CONV decfloat34( '123.456789' ). " Decimal Float 34
DATA(lv_c10) = CONV c LENGTH 10( 'Test' ). " Char 10
DATA(lv_n5) = CONV n LENGTH 5( 42 ). " NUMC 5

10. CONV pour éviter les erreurs d’exécution

" Potentiellement erroné sans vérification
DATA: lv_input TYPE string VALUE 'abc'.
" Intercepter avec TRY-CATCH
TRY.
DATA(lv_number) = CONV i( lv_input ).
CATCH cx_sy_conversion_no_number.
WRITE: / 'Pas un nombre valide'.
ENDTRY.

CAST – Casting de références d’objets

L’opérateur CAST effectue un downcast ou crosscast de références d’objets.

Syntaxe

CAST <type_cible>( <reference_objet> )
CAST #( <reference_objet> ) " Le type est déduit du contexte

Exemples pour CAST

1. Downcast (Classe de base vers sous-classe)

CLASS lcl_vehicle DEFINITION.
PUBLIC SECTION.
METHODS: get_type RETURNING VALUE(rv_type) TYPE string.
ENDCLASS.
CLASS lcl_car DEFINITION INHERITING FROM lcl_vehicle.
PUBLIC SECTION.
METHODS: get_brand RETURNING VALUE(rv_brand) TYPE string.
DATA: mv_brand TYPE string.
ENDCLASS.
" Upcast : Car → Vehicle (implicite)
DATA: lo_vehicle TYPE REF TO lcl_vehicle.
lo_vehicle = NEW lcl_car( ).
" Downcast : Vehicle → Car (explicite avec CAST)
DATA(lo_car) = CAST lcl_car( lo_vehicle ).
DATA(lv_brand) = lo_car->get_brand( ).

2. Comparaison : CAST vs. casting classique

" === CLASSIQUE ===
DATA: lo_car_classic TYPE REF TO lcl_car.
lo_car_classic ?= lo_vehicle. " Downcast avec ?=
" === MODERNE AVEC CAST ===
DATA(lo_car_modern) = CAST lcl_car( lo_vehicle ).
" Ou avec déduction de type
DATA: lo_target TYPE REF TO lcl_car.
lo_target = CAST #( lo_vehicle ).

3. CAST avec Interface

INTERFACE lif_printable.
METHODS: print.
ENDINTERFACE.
CLASS lcl_document DEFINITION.
PUBLIC SECTION.
INTERFACES: lif_printable.
ENDCLASS.
" Créer un objet
DATA: lo_doc TYPE REF TO lcl_document.
lo_doc = NEW #( ).
" Upcast vers interface
DATA: lo_printable TYPE REF TO lif_printable.
lo_printable = lo_doc.
" Downcast retour vers la classe
DATA(lo_doc_back) = CAST lcl_document( lo_printable ).

4. CAST pour chaînage de méthodes

" Sans CAST : Variable intermédiaire nécessaire
DATA: lo_base TYPE REF TO lcl_vehicle.
lo_base = get_vehicle( ).
DATA: lo_car TYPE REF TO lcl_car.
lo_car ?= lo_base.
lo_car->start_engine( ).
" Avec CAST : Casting inline
CAST lcl_car( get_vehicle( ) )->start_engine( ).

5. CAST dans les expressions

" Accès direct aux attributs de sous-classe
DATA(lv_brand) = CAST lcl_car( lo_vehicle )->mv_brand.
" Dans les String Templates
DATA(lv_info) = |Marque : { CAST lcl_car( lo_vehicle )->mv_brand }|.
" Dans les conditions
IF CAST lcl_car( lo_vehicle )->mv_brand = 'BMW'.
WRITE: / 'BMW trouvée'.
ENDIF.

6. Casting sécurisé avec IS INSTANCE OF

" D'abord vérifier, puis caster
IF lo_vehicle IS INSTANCE OF lcl_car.
DATA(lo_safe_car) = CAST lcl_car( lo_vehicle ).
lo_safe_car->start_engine( ).
ENDIF.
" Avec CASE TYPE OF
CASE TYPE OF lo_vehicle.
WHEN TYPE lcl_car.
CAST lcl_car( lo_vehicle )->start_engine( ).
WHEN TYPE lcl_truck.
CAST lcl_truck( lo_vehicle )->load_cargo( ).
WHEN OTHERS.
WRITE: / 'Type de véhicule inconnu'.
ENDCASE.

7. CAST avec types génériques

" Avec DATA comme référence générique
DATA: lr_any TYPE REF TO data.
DATA: lt_table TYPE TABLE OF string.
lr_any = REF #( lt_table ).
" Cast vers type de table concret
FIELD-SYMBOLS: <lt_strings> TYPE TABLE OF string.
ASSIGN CAST #( lr_any )->* TO <lt_strings>.

8. CAST avec RTTS (Runtime Type Services)

DATA: lo_type TYPE REF TO cl_abap_typedescr.
lo_type = cl_abap_typedescr=>describe_by_data( lv_string ).
" Cast vers Descriptor spécifique
IF lo_type->kind = cl_abap_typedescr=>kind_elem.
DATA(lo_elem) = CAST cl_abap_elemdescr( lo_type ).
WRITE: / 'Longueur :', lo_elem->output_length.
ENDIF.

9. CAST pour le pattern Factory

CLASS lcl_vehicle_factory DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: create
IMPORTING iv_type TYPE string
RETURNING VALUE(ro_vehicle) TYPE REF TO lcl_vehicle.
ENDCLASS.
CLASS lcl_vehicle_factory IMPLEMENTATION.
METHOD create.
ro_vehicle = SWITCH #( iv_type
WHEN 'CAR' THEN NEW lcl_car( )
WHEN 'TRUCK' THEN NEW lcl_truck( )
ELSE NEW lcl_vehicle( )
).
ENDMETHOD.
ENDCLASS.
" Utilisation avec CAST
DATA(lo_my_car) = CAST lcl_car(
lcl_vehicle_factory=>create( 'CAR' )
).

10. Intercepter CX_SY_MOVE_CAST_ERROR

TRY.
" Cast erroné (Vehicle n'est pas un Car)
DATA: lo_plain TYPE REF TO lcl_vehicle.
lo_plain = NEW lcl_vehicle( ).
DATA(lo_wrong) = CAST lcl_car( lo_plain ). " ERREUR !
CATCH cx_sy_move_cast_error INTO DATA(lx_error).
WRITE: / 'Cast échoué :', lx_error->get_text( ).
ENDTRY.

CONV vs. CAST

AspectCONVCAST
ButConversion de types de donnéesCasting de références d’objets
ApplicationTypes élémentaires, structuresRéférences de classes, interfaces
ExempleCONV i( '42' )CAST lcl_car( lo_vehicle )
ErreurCX_SY_CONVERSION_*CX_SY_MOVE_CAST_ERROR
ClassiqueConversion impliciteOpérateur ?=

Fonctions de conversion intégrées

En plus de CONV, il existe des fonctions intégrées :

" Conversion booléenne
DATA(lv_bool) = xsdbool( lv_age >= 18 ). " abap_true/abap_false
" Booléen conditionnel
DATA(lv_cond) = boolc( lv_status = 'A' ).
" Conversion Alpha (dans les String Templates)
DATA(lv_matnr) = |{ lv_material ALPHA = OUT }|.
" Valeur absolue
DATA(lv_abs) = abs( -42 ). " 42
" Signe
DATA(lv_sign) = sign( -5 ). " -1
" Arrondi
DATA(lv_round) = round( val = '3.7' dec = 0 ). " 4
" Minimum/Maximum
DATA(lv_min) = nmin( val1 = 10 val2 = 5 ). " 5
DATA(lv_max) = nmax( val1 = 10 val2 = 5 ). " 10

Notes importantes / Bonnes pratiques

  • CONV pour les conversions de types de données – remplace les conversions implicites de manière explicite.
  • CAST pour le casting de références d’objets – remplace l’opérateur ?=.
  • Vérifiez avec IS INSTANCE OF avant un CAST pour éviter les erreurs d’exécution.
  • CONV #() et CAST #() déduisent le type cible du contexte.
  • Utilisez TRY-CATCH pour les conversions incertaines.
  • CONV permet des conversions inline dans les expressions et appels de méthodes.
  • CAST permet le chaînage de méthodes sans variables intermédiaires.
  • Combinez avec VALUE, COND et SWITCH.
  • Pour les calculs décimaux : Convertissez avant la division pour des résultats précis.
  • Utilisez les fonctions intégrées comme xsdbool(), abs(), round() pour les conversions fréquentes.