ABAP CONV und CAST: Typkonvertierung und Casting

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Die Konstruktorausdrücke CONV und CAST ermöglichen Typkonvertierungen inline. CONV konvertiert elementare Datentypen, während CAST für das Casting von Objektreferenzen verwendet wird.

CONV – Typkonvertierung

Der CONV-Operator konvertiert einen Wert in einen anderen Datentyp.

Syntax

CONV <zieltyp>( <ausdruck> )
CONV #( <ausdruck> ) " Typ wird aus Kontext abgeleitet

Beispiele für CONV

1. Grundlegende Typkonvertierung

" String zu Integer
DATA(lv_int) = CONV i( '42' ).
WRITE: / lv_int. " 42
" Integer zu String
DATA(lv_str) = CONV string( 123 ).
WRITE: / lv_str. " 123
" Packed zu 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. Vergleich: CONV vs. klassische Konvertierung

" === KLASSISCH ===
DATA: lv_string TYPE string VALUE '100',
lv_number TYPE i.
lv_number = lv_string. " Implizite Konvertierung
" Oder mit MOVE
MOVE lv_string TO lv_number.
" === MODERN MIT CONV ===
DATA(lv_number2) = CONV i( '100' ).
" Inline in Ausdrücken
DATA(lv_result) = CONV i( '50' ) + CONV i( '30' ).
WRITE: / lv_result. " 80

3. CONV für Dezimalzahlen

" String zu Packed Decimal
DATA(lv_amount) = CONV p DECIMALS 2( '1234.56' ).
WRITE: / lv_amount. " 1234.56
" Division mit korrektem Ergebnis
DATA: lv_a TYPE i VALUE 10,
lv_b TYPE i VALUE 3.
" Ohne CONV: Ganzzahl-Division
DATA(lv_div1) = lv_a / lv_b.
WRITE: / lv_div1. " 3 (abgeschnitten)
" Mit CONV: Dezimalergebnis
DATA(lv_div2) = CONV decfloat34( lv_a ) / lv_b.
WRITE: / lv_div2. " 3.333...

4. CONV in Berechnungen

DATA: lv_quantity TYPE i VALUE 7,
lv_total TYPE i VALUE 100.
" Prozentberechnung mit Dezimalergebnis
DATA(lv_percentage) = CONV decfloat16( lv_quantity * 100 / lv_total ).
WRITE: / lv_percentage, '%'. " 7%
" Präzise Berechnung
DATA(lv_price) = CONV p DECIMALS 2( 100 / 3 ).
WRITE: / lv_price. " 33.33

5. CONV für Zeichenketten

" Char zu String
DATA: lv_char TYPE c LENGTH 10 VALUE 'Test'.
DATA(lv_string) = CONV string( lv_char ).
" Numerisch zu formatiertem String
DATA: lv_num TYPE i VALUE 42.
DATA(lv_formatted) = |Nummer: { CONV string( lv_num ) }|.
" NUMC zu 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 mit #-Notation (Typableitung)

METHODS: process_amount
IMPORTING iv_amount TYPE p DECIMALS 2.
" Typ wird aus Parameter abgeleitet
process_amount( iv_amount = CONV #( '99.99' ) ).
" Bei Zuweisungen
DATA: lv_target TYPE p DECIMALS 2.
lv_target = CONV #( '123.45' ). " Typ aus lv_target abgeleitet

7. CONV für Datum und Zeit

" String zu Datum
DATA(lv_date) = CONV d( '20241115' ).
WRITE: / lv_date. " 20241115
" Timestamp
DATA(lv_ts) = CONV timestamp( '20241115143000' ).
" Datum aus Teilen zusammensetzen
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 in Methodenaufrufen

METHODS: get_discount
IMPORTING iv_amount TYPE p DECIMALS 2
RETURNING VALUE(rv_discount) TYPE p DECIMALS 2.
" Direkte Konvertierung beim Aufruf
DATA(lv_discount) = get_discount( iv_amount = CONV #( 100 ) ).
" Bei mehreren Parametern
calculate(
iv_quantity = CONV i( '5' )
iv_price = CONV p DECIMALS 2( '19.99' )
).

9. CONV mit Built-in Types

" Verschiedene Typen
DATA(lv_i) = CONV i( '42' ). " Integer
DATA(lv_i8) = CONV int8( '9999999999' ). " 8-Byte Integer
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 zur Vermeidung von Laufzeitfehlern

" Potentiell fehlerhaft ohne Prüfung
DATA: lv_input TYPE string VALUE 'abc'.
" Mit TRY-CATCH abfangen
TRY.
DATA(lv_number) = CONV i( lv_input ).
CATCH cx_sy_conversion_no_number.
WRITE: / 'Keine gültige Zahl'.
ENDTRY.

CAST – Objektreferenz-Casting

Der CAST-Operator führt ein Downcast oder Crosscast von Objektreferenzen durch.

Syntax

CAST <zieltyp>( <objektreferenz> )
CAST #( <objektreferenz> ) " Typ wird aus Kontext abgeleitet

Beispiele für CAST

1. Downcast (Basisklasse → Unterklasse)

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 (implizit)
DATA: lo_vehicle TYPE REF TO lcl_vehicle.
lo_vehicle = NEW lcl_car( ).
" Downcast: Vehicle → Car (explizit mit CAST)
DATA(lo_car) = CAST lcl_car( lo_vehicle ).
DATA(lv_brand) = lo_car->get_brand( ).

2. Vergleich: CAST vs. klassisches Casting

" === KLASSISCH ===
DATA: lo_car_classic TYPE REF TO lcl_car.
lo_car_classic ?= lo_vehicle. " Downcast mit ?=
" === MODERN MIT CAST ===
DATA(lo_car_modern) = CAST lcl_car( lo_vehicle ).
" Oder mit Typ-Ableitung
DATA: lo_target TYPE REF TO lcl_car.
lo_target = CAST #( lo_vehicle ).

3. CAST mit Interface

INTERFACE lif_printable.
METHODS: print.
ENDINTERFACE.
CLASS lcl_document DEFINITION.
PUBLIC SECTION.
INTERFACES: lif_printable.
ENDCLASS.
" Objekt erstellen
DATA: lo_doc TYPE REF TO lcl_document.
lo_doc = NEW #( ).
" Upcast zu Interface
DATA: lo_printable TYPE REF TO lif_printable.
lo_printable = lo_doc.
" Downcast zurück zur Klasse
DATA(lo_doc_back) = CAST lcl_document( lo_printable ).

4. CAST für Methodenverkettung

" Ohne CAST: Zwischenvariable nötig
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( ).
" Mit CAST: Inline-Casting
CAST lcl_car( get_vehicle( ) )->start_engine( ).

5. CAST in Ausdrücken

" Direkter Zugriff auf Unterklassen-Attribute
DATA(lv_brand) = CAST lcl_car( lo_vehicle )->mv_brand.
" In String Templates
DATA(lv_info) = |Brand: { CAST lcl_car( lo_vehicle )->mv_brand }|.
" In Bedingungen
IF CAST lcl_car( lo_vehicle )->mv_brand = 'BMW'.
WRITE: / 'BMW gefunden'.
ENDIF.

6. Sicheres Casting mit IS INSTANCE OF

" Erst prüfen, dann casten
IF lo_vehicle IS INSTANCE OF lcl_car.
DATA(lo_safe_car) = CAST lcl_car( lo_vehicle ).
lo_safe_car->start_engine( ).
ENDIF.
" Mit 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: / 'Unbekannter Fahrzeugtyp'.
ENDCASE.

7. CAST mit generischen Typen

" Mit DATA als generischer Referenz
DATA: lr_any TYPE REF TO data.
DATA: lt_table TYPE TABLE OF string.
lr_any = REF #( lt_table ).
" Cast zu konkretem Tabellentyp
FIELD-SYMBOLS: <lt_strings> TYPE TABLE OF string.
ASSIGN CAST #( lr_any )->* TO <lt_strings>.

8. CAST bei RTTS (Runtime Type Services)

DATA: lo_type TYPE REF TO cl_abap_typedescr.
lo_type = cl_abap_typedescr=>describe_by_data( lv_string ).
" Cast zu spezifischem Descriptor
IF lo_type->kind = cl_abap_typedescr=>kind_elem.
DATA(lo_elem) = CAST cl_abap_elemdescr( lo_type ).
WRITE: / 'Länge:', lo_elem->output_length.
ENDIF.

9. CAST für Factory-Pattern

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.
" Verwendung mit CAST
DATA(lo_my_car) = CAST lcl_car(
lcl_vehicle_factory=>create( 'CAR' )
).

10. CX_SY_MOVE_CAST_ERROR abfangen

TRY.
" Fehlerhafter Cast (Vehicle ist kein Car)
DATA: lo_plain TYPE REF TO lcl_vehicle.
lo_plain = NEW lcl_vehicle( ).
DATA(lo_wrong) = CAST lcl_car( lo_plain ). " FEHLER!
CATCH cx_sy_move_cast_error INTO DATA(lx_error).
WRITE: / 'Cast fehlgeschlagen:', lx_error->get_text( ).
ENDTRY.

CONV vs. CAST

AspektCONVCAST
ZweckDatentypkonvertierungObjektreferenz-Casting
AnwendungElementare Typen, StrukturenKlassenreferenzen, Interfaces
BeispielCONV i( '42' )CAST lcl_car( lo_vehicle )
FehlerCX_SY_CONVERSION_*CX_SY_MOVE_CAST_ERROR
KlassischImplizite Konvertierung?= Operator

Built-in Konvertierungsfunktionen

Neben CONV gibt es eingebaute Funktionen:

" Boolean-Konvertierung
DATA(lv_bool) = xsdbool( lv_age >= 18 ). " abap_true/abap_false
" Bedingter Boolean
DATA(lv_cond) = boolc( lv_status = 'A' ).
" Alpha-Konvertierung (in String Templates)
DATA(lv_matnr) = |{ lv_material ALPHA = OUT }|.
" Absoluter Wert
DATA(lv_abs) = abs( -42 ). " 42
" Vorzeichen
DATA(lv_sign) = sign( -5 ). " -1
" Rundung
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

Wichtige Hinweise / Best Practice

  • CONV für Datentypkonvertierungen – ersetzt implizite Konvertierungen explizit.
  • CAST für Objektreferenz-Casting – ersetzt den ?= Operator.
  • Prüfen Sie mit IS INSTANCE OF vor einem CAST zur Vermeidung von Laufzeitfehlern.
  • CONV #() und CAST #() leiten den Zieltyp aus dem Kontext ab.
  • Verwenden Sie TRY-CATCH bei unsicheren Konvertierungen.
  • CONV ermöglicht Inline-Konvertierungen in Ausdrücken und Methodenaufrufen.
  • CAST erlaubt Methodenverkettung ohne Zwischenvariablen.
  • Kombinieren Sie mit VALUE, COND und SWITCH.
  • Bei Dezimalberechnungen: Konvertieren Sie vor der Division für präzise Ergebnisse.
  • Nutzen Sie Built-in Functions wie xsdbool(), abs(), round() für häufige Konvertierungen.