ABAP RAISE Statement: Ausnahmen auslösen (klassenbasiert & nicht-klassenbasiert)

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

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

  1. Ein Objekt der angegebenen <Ausnahmeklasse> wird erzeugt. Dabei können über EXPORTING Werte an den Konstruktor der Klasse übergeben werden, um Details zum Fehler mitzugeben (z. B. der fehlerhafte Wert, eine ID).
  2. 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 Interface IF_T100_MESSAGE).
  3. Der normale Programmfluss wird gestoppt.
  4. Das System sucht im Aufrufstapel nach oben nach einem TRY...CATCH-Block, der diese Ausnahmeklasse (oder eine ihrer Oberklassen) behandeln kann.
  5. Wird ein CATCH-Block gefunden, wird dessen Code ausgeführt.
  6. 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

  1. Die Ausführung der Prozedur wird sofort beendet.
  2. Das System setzt das Systemfeld sy-subrc auf einen Wert, der im EXCEPTIONS-Zusatz des Aufrufers ( CALL FUNCTION ... EXCEPTIONS <ausnahme_name> = wert ...) diesem <ausnahme_name> zugeordnet ist.
  3. 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_MESSAGE
DATA 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 oder CX_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 (insbesondere CX_NO_CHECK-basierte).
  • Vermeide RAISE <exception_name> in neuem Code, es sei denn zur Interaktion mit sehr alten Funktionsbausteinen.