Sentencia RAISE en ABAP: Lanzar Excepciones (basadas en clase y no basadas en clase)

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Estas sentencias se utilizan para señalar activamente una situación de excepción (Exception) en el flujo del programa. Lo haces típicamente cuando:

  • Ocurre un error que la unidad de programa actual (método, módulo de función, form routine) no puede resolver por sí misma.
  • Se alcanza un estado inesperado o inválido.
  • Se viola una determinada regla de negocio.

Lanzar una excepción interrumpe el flujo normal del programa en ese punto y pasa el control a un bloque especial de manejo de errores (bloque CATCH para excepciones basadas en clase) o al llamador (que luego verifica sy-subrc para excepciones no basadas en clase). Esto permite un manejo estructurado de errores.

Hay dos tipos principales de excepciones y por tanto dos variantes de RAISE:

1. Excepciones Basadas en Clase (Moderno, ¡Preferido!)

Este es el método estándar en ABAP moderno y orientado a objetos. Las excepciones aquí son objetos de clases de excepción especiales (que heredan de CX_ROOT).

Sintaxis

" 1. Lanzar una nueva excepción de una clase:
RAISE EXCEPTION TYPE <ClaseExcepcion>
[EXPORTING p1 = v1 p2 = v2 ...] " Pasar parámetros al constructor
[MESSAGE { <msg> " Mensaje desde objeto/interfaz
| ID <id> TYPE <t> NUMBER <n> [WITH v1 v2 v3 v4] " Mensaje desde T100
| oref->if_t100_message } ] " Mensaje desde otro objeto
[USING MESSAGE]. " Usar mensaje desde TEXTID
" 2. Relanzar un objeto de excepción ya existente (p.ej. en CATCH):
RAISE EXCEPTION <objeto_excepcion>.

Funcionamiento

  1. Se crea un objeto de la <ClaseExcepcion> especificada. A través de EXPORTING se pueden pasar valores al constructor de la clase para proporcionar detalles sobre el error (p.ej., el valor erróneo, un ID).
  2. Opcionalmente, a través de la adición MESSAGE se puede vincular un mensaje de la gestión de mensajes (SE91) u otro objeto con la excepción (muchas clases de excepción implementan para esto la interfaz IF_T100_MESSAGE).
  3. El flujo normal del programa se detiene.
  4. El sistema busca hacia arriba en la pila de llamadas un bloque TRY...CATCH que pueda manejar esta clase de excepción (o una de sus superclases).
  5. Si se encuentra un bloque CATCH, se ejecuta su código.
  6. Si no se encuentra ningún bloque CATCH adecuado, esto lleva a un error en tiempo de ejecución (short dump).

Clases de Excepción: Se crean en SE24 y típicamente heredan de

  • CX_STATIC_CHECK: Errores que potencialmente podrían ser reconocidos en tiempo de desarrollo por la verificación de sintaxis (pero no tienen que serlo). Normalmente deberían manejarse.
  • CX_DYNAMIC_CHECK: Errores que solo ocurren en tiempo de ejecución, pero que deberían ser esperados y manejados por el programa.
  • CX_NO_CHECK: Errores graves e inesperados, donde el manejo no está necesariamente previsto (a menudo lleva a dump si no se captura explícitamente).
  • Manejo de errores: El llamador captura estas excepciones con TRY. ... CATCH cx_... INTO DATA(lo_exc). ... ENDTRY.

2. Excepciones No Basadas en Clase (Obsoleto)

Este es el método más antiguo, que se usa principalmente en módulos de función y métodos más antiguos.

Contexto: Solo tiene sentido en procedimientos (módulos de función, métodos, FORMs) que declaran tales excepciones en la adición EXCEPTIONS de su interfaz.

Sintaxis

RAISE <nombre_excepcion>.

Funcionamiento

  1. La ejecución del procedimiento se termina inmediatamente.
  2. El sistema establece el campo del sistema sy-subrc a un valor que está asignado a este <nombre_excepcion> en la adición EXCEPTIONS del llamador (CALL FUNCTION ... EXCEPTIONS <nombre_excepcion> = valor ...).
  3. El llamador debe verificar sy-subrc después de la llamada para determinar si esta excepción ocurrió.
  • Estado: Se considera obsoleto para nuevos desarrollos, especialmente en el contexto orientado a objetos. Solo debería usarse cuando módulos más antiguos lo requieran.

Ejemplos

1: Lanzar y Capturar Excepción Basada en Clase

" Clase de excepción propia (simplificada)
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.
" Lanza nuestra propia excepción
RAISE EXCEPTION TYPE zcx_division_by_zero.
ELSE.
result = dividend / divisor.
WRITE: / 'Resultado:', result.
ENDIF.
CATCH zcx_division_by_zero INTO lo_error.
MESSAGE 'Error: ¡División por cero!' TYPE 'E'.
" Opcional: Acceder a atributos/métodos de lo_error
ENDTRY.
WRITE / 'El programa continúa después del bloque TRY.'.

2: Excepción Basada en Clase con Mensaje

" Suposición: Mensaje 010 en clase ZMSG: 'El material &1 no está activo.'
" Suposición: ZCX_MATERIAL_INACTIVE implementa IF_T100_MESSAGE
DATA lv_matnr TYPE matnr VALUE 'M-INACTIVE'.
DATA lo_mat_error TYPE REF TO zcx_material_inactive.
TRY.
" ... La verificación resulta que el material está inactivo ...
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'. " Obtener mensaje de la excepción
ENDTRY.

3: Excepción No Basada en Clase (Concepto en Módulo de Función)

FUNCTION Z_DO_SOMETHING.
*"----------------------------------------------------------------------
*"*"Interfaz local:
*" IMPORTING
*" VALUE(INPUT) TYPE I
*" EXPORTING
*" VALUE(OUTPUT) TYPE I
*" EXCEPTIONS
*" INPUT_IS_ZERO " Excepción declarada
*"----------------------------------------------------------------------
IF input = 0.
RAISE input_is_zero. " lanza la excepción -> sy-subrc se establece en el llamador
ELSE.
output = 100 / input.
ENDIF.
ENDFUNCTION.
" Programa que llama:
DATA result TYPE i.
CALL FUNCTION 'Z_DO_SOMETHING'
EXPORTING
input = 0
IMPORTING
OUTPUT = result
EXCEPTIONS
input_is_zero = 1 " Mapea excepción a sy-subrc = 1
OTHERS = 2.
IF sy-subrc = 1.
MESSAGE '¡La entrada para Z_DO_SOMETHING fue cero!' TYPE 'W'.
ELSEIF sy-subrc = 0.
WRITE: / 'Resultado:', result.
ENDIF.

Notas Importantes / Mejores Prácticas

  • Usa excepciones basadas en clase (RAISE EXCEPTION TYPE ...) para todos los nuevos desarrollos.
  • Define clases de excepción propias descriptivas para casos de error específicos de tu aplicación. Hereda de CX_STATIC_CHECK o CX_DYNAMIC_CHECK.
  • Implementa la interfaz IF_T100_MESSAGE en tus clases de excepción para vincularlas fácilmente con clases de mensajes (SE91).
  • Pasa información de contexto relevante a través de la adición EXPORTING al constructor de la clase de excepción.
  • Captura excepciones de forma selectiva con TRY...CATCH...ENDTRY donde puedan manejarse de forma significativa. Deja que las excepciones no manejables lleven conscientemente a un dump (especialmente las basadas en CX_NO_CHECK).
  • Evita RAISE <nombre_excepcion> en código nuevo, a menos que sea para interactuar con módulos de función muy antiguos.