Diese Anweisungen werden verwendet, um im Programmablauf aktiv eine Ausnahmesituation (Exception) zu signalisieren. Das tust Du typischerweise dann, wenn:
- Ein Fehler auftritt, den die aktuelle Programmeinheit (Methode, Funktionsbaustein, Form-Routine) nicht selbst beheben kann.
- Ein unerwarteter oder ungültiger Zustand erreicht wird.
- Eine bestimmte Geschäftsregel verletzt wird.
Das Auslösen einer Ausnahme unterbricht den normalen Programmfluss an dieser Stelle und gibt die Kontrolle an einen
speziellen Fehlerbehandlungsblock (CATCH
-Block bei klassenbasierten Ausnahmen) oder an den Aufrufer (der dann
sy-subrc
prüft bei nicht-klassenbasierten Ausnahmen) weiter. Dies ermöglicht eine strukturierte Fehlerbehandlung.
Es gibt zwei Hauptarten von Ausnahmen und damit zwei RAISE
-Varianten:
1. Klassenbasierte Ausnahmen (Modern, Bevorzugt!)
Dies ist der Standardweg in modernem, objektorientiertem ABAP. Ausnahmen sind hier Objekte von speziellen
Ausnahmeklassen (die von CX_ROOT
erben).
Syntax
" 1. Neue Ausnahme einer Klasse auslösen:RAISE EXCEPTION TYPE <Ausnahmeklasse> [EXPORTING p1 = v1 p2 = v2 ...] " Parameter an Konstruktor übergeben [MESSAGE { <msg> " Nachricht aus Objekt/Interface | ID <id> TYPE <t> NUMBER <n> [WITH v1 v2 v3 v4] " Nachricht aus T100 | oref->if_t100_message } ] " Nachricht aus anderem Objekt [USING MESSAGE]. " Nachricht aus TEXTID verwenden
" 2. Bereits existierendes Ausnahmeobjekt erneut auslösen (z.B. in CATCH):RAISE EXCEPTION <ausnahme_objekt>.
Funktionsweise
- Ein Objekt der angegebenen
<Ausnahmeklasse>
wird erzeugt. Dabei können überEXPORTING
Werte an den Konstruktor der Klasse übergeben werden, um Details zum Fehler mitzugeben (z. B. der fehlerhafte Wert, eine ID). - Optional kann über den
MESSAGE
-Zusatz eine Nachricht aus der Nachrichtenpflege (SE91
) oder einem anderen Objekt mit der Ausnahme verknüpft werden (viele Ausnahmeklassen implementieren dazu das InterfaceIF_T100_MESSAGE
). - Der normale Programmfluss wird gestoppt.
- Das System sucht im Aufrufstapel nach oben nach einem
TRY...CATCH
-Block, der diese Ausnahmeklasse (oder eine ihrer Oberklassen) behandeln kann. - Wird ein
CATCH
-Block gefunden, wird dessen Code ausgeführt. - Wird kein passender
CATCH
-Block gefunden, führt dies zu einem Laufzeitfehler (Kurzdump).
Exception-Klassen:** Werden in SE24
angelegt und erben typischerweise von
CX_STATIC_CHECK
: Fehler, die potenziell schon zur Entwicklungszeit durch die Syntaxprüfung erkannt werden könnten ( aber nicht müssen). Sollten normalerweise behandelt werden.CX_DYNAMIC_CHECK
: Fehler, die erst zur Laufzeit auftreten, aber vom Programm erwartet und behandelt werden sollten.CX_NO_CHECK
: Schwerwiegende, unerwartete Fehler, bei denen eine Behandlung nicht unbedingt vorgesehen ist (führt oft zum Dump, wenn nicht explizit gefangen).- Fehlerbehandlung: Der Aufrufer fängt diese Ausnahmen mit
TRY. ... CATCH cx_... INTO DATA(lo_exc). ... ENDTRY.
2. Nicht-klassenbasierte Ausnahmen (Veraltet)
Dies ist die ältere Methode, die vor allem in Funktionsbausteinen und älteren Methoden verwendet wird.
Kontext: Nur sinnvoll in Prozeduren (Funktionsbausteinen, Methoden, FORMs), die im EXCEPTIONS
-Zusatz ihrer
Schnittstelle solche Ausnahmen deklarieren.
Syntax
RAISE <ausnahme_name>.
Funktionsweise
- Die Ausführung der Prozedur wird sofort beendet.
- Das System setzt das Systemfeld
sy-subrc
auf einen Wert, der imEXCEPTIONS
-Zusatz des Aufrufers (CALL FUNCTION ... EXCEPTIONS <ausnahme_name> = wert ...
) diesem<ausnahme_name>
zugeordnet ist. - Der Aufrufer muss
sy-subrc
nach dem Aufruf prüfen, um festzustellen, ob diese Ausnahme aufgetreten ist.
- Status: Gilt als veraltet für Neuentwicklungen, insbesondere im objektorientierten Kontext. Sollte nur noch verwendet werden, wenn ältere Bausteine dies erfordern.
Beispiele
1: Klassenbasierte Ausnahme auslösen und fangen
" Eigene Ausnahmeklasse (vereinfacht)CLASS zcx_division_by_zero DEFINITION INHERITING FROM cx_static_check.ENDCLASS.CLASS zcx_division_by_zero IMPLEMENTATION.ENDCLASS.
START-OF-SELECTION. DATA dividend TYPE i VALUE 10. DATA divisor TYPE i VALUE 0. DATA result TYPE f. DATA lo_error TYPE REF TO zcx_division_by_zero.
TRY. IF divisor = 0. " Löse unsere eigene Ausnahme aus RAISE EXCEPTION TYPE zcx_division_by_zero. ELSE. result = dividend / divisor. WRITE: / 'Ergebnis:', result. ENDIF.
CATCH zcx_division_by_zero INTO lo_error. MESSAGE 'Fehler: Division durch Null!' TYPE 'E'. " Optional: Zugriff auf Attribute/Methoden von lo_error ENDTRY.
WRITE / 'Programm läuft weiter nach TRY-Block.'.
2: Klassenbasierte Ausnahme mit Nachricht
" Annahme: Nachricht 010 in Klasse ZMSG: 'Material &1 ist nicht aktiv.'" Annahme: ZCX_MATERIAL_INACTIVE implementiert IF_T100_MESSAGEDATA lv_matnr TYPE matnr VALUE 'M-INACTIVE'.DATA lo_mat_error TYPE REF TO zcx_material_inactive.
TRY. " ... Prüfung ergibt, dass Material inaktiv ist ... RAISE EXCEPTION TYPE zcx_material_inactive MESSAGE ID 'ZMSG' TYPE 'E' NUMBER '010' WITH lv_matnr.
CATCH zcx_material_inactive INTO lo_mat_error. MESSAGE lo_mat_error->get_text( ) TYPE 'E'. " Nachricht aus Exception holen ENDTRY.
3: Nicht-klassenbasierte Ausnahme (Konzept in Fuba)
FUNCTION Z_DO_SOMETHING.*"----------------------------------------------------------------------*"*"Lokale Schnittstelle:*" IMPORTING*" VALUE(INPUT) TYPE I*" EXPORTING*" VALUE(OUTPUT) TYPE I*" EXCEPTIONS*" INPUT_IS_ZERO " Deklarierte Ausnahme*"---------------------------------------------------------------------- IF input = 0. RAISE input_is_zero. " löst die Ausnahme aus -> sy-subrc wird beim Aufrufer gesetzt ELSE. output = 100 / input. ENDIF.ENDFUNCTION.
" Aufrufendes Programm:DATA result TYPE i.CALL FUNCTION 'Z_DO_SOMETHING' EXPORTING input = 0 IMPORTING OUTPUT = result EXCEPTIONS input_is_zero = 1 " Mappt Ausnahme auf sy-subrc = 1 OTHERS = 2.
IF sy-subrc = 1. MESSAGE 'Eingabe für Z_DO_SOMETHING war Null!' TYPE 'W'.ELSEIF sy-subrc = 0. WRITE: / 'Ergebnis:', result.ENDIF.
Wichtige Hinweise / Best Practice
- Nutze klassenbasierte Ausnahmen (
RAISE EXCEPTION TYPE ...
) für alle neuen Entwicklungen. - Definiere sprechende eigene Ausnahmeklassen für spezifische Fehlerfälle Deiner Anwendung. Leite sie von
CX_STATIC_CHECK
oderCX_DYNAMIC_CHECK
ab. - Implementiere das Interface
IF_T100_MESSAGE
in Deinen Ausnahmeklassen, um sie einfach mit Nachrichtenklassen ( SE91) zu verknüpfen. - Übergib relevante Kontextinformationen über den
EXPORTING
-Zusatz an den Konstruktor der Ausnahmeklasse. - Fange Ausnahmen gezielt mit
TRY...CATCH...ENDTRY
ab, wo sie sinnvoll behandelt werden können. Lass unbehandelbare Ausnahmen bewusst zu einem Dump führen (insbesondereCX_NO_CHECK
-basierte). - Vermeide
RAISE <exception_name>
in neuem Code, es sei denn zur Interaktion mit sehr alten Funktionsbausteinen.