Винятки
Виняток (exception) — штатний спосіб сигналізувати про ненормальну ситуацію: бракує даних, помилка конвертації, відмова зовнішнього сервісу. У сучасному ABAP використовуються class-based exceptions — усі спадкуються від cx_root. Старі sy-subrc-based винятки у function modules (EXCEPTIONS ... = ...) — legacy, не для нового коду.
Ключова ідея: метод, що може впасти, декларує це у RAISING; викликач або обробляє через TRY/CATCH, або пробрасує далі. Компілятор перевіряє цей контракт для cx_static_check.
Категорії винятків
Section titled “Категорії винятків”| Клас-база | Перевірка | Коли використовувати |
|---|---|---|
cx_static_check | Компілятор вимагає RAISING або TRY/CATCH | Очікувані помилки, які викликач має свідомо обробити (валідація, бракує запису) |
cx_dynamic_check | Лише в runtime | Ситуації, яких у нормальному потоці не має бути (інваріант, арифметичні переповнення) |
cx_no_check | Не потребує декларації | Системні критичні; використовуй дуже рідко у власних класах |
Власний клас винятку
Section titled “Власний клас винятку”CLASS zcx_not_found DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_t100_dyn_msg. " для інтеграції з T100-повідомленнями INTERFACES if_t100_message.
CONSTANTS: BEGIN OF no_data, msgid TYPE symsgid VALUE 'ZDEMO', msgno TYPE symsgno VALUE '001', attr1 TYPE scx_attrname VALUE 'ID', END OF no_data.
METHODS constructor IMPORTING textid LIKE if_t100_message=>t100key OPTIONAL previous LIKE previous OPTIONAL id TYPE string OPTIONAL.
DATA id TYPE string.ENDCLASS.
CLASS zcx_not_found IMPLEMENTATION. METHOD constructor. super->constructor( previous = previous ). me->id = id. IF textid IS INITIAL. if_t100_message~t100key = no_data. ELSE. if_t100_message~t100key = textid. ENDIF. ENDMETHOD.ENDCLASS.Ключові моменти:
- Спадкування від
cx_static_check/cx_dynamic_check/cx_no_checkвизначає категорію. super->constructor( previous = previous )— пробрасування попереднього винятку (exception chaining).- Для текстів повідомлень використовуй
if_t100_message(TOBJECT-повідомлення) абоIF_T100_DYN_MSG(з параметрами).
Викидання — RAISE EXCEPTION / THROW
Section titled “Викидання — RAISE EXCEPTION / THROW”" Форма statementRAISE EXCEPTION TYPE zcx_not_found EXPORTING id = `42`.
" Forma виразу — всередині constructor expressionresult = COND #( WHEN id IS INITIAL THEN THROW zcx_not_found( id = `empty` ) ELSE load( id ) ).
" З текстом повідомленняRAISE EXCEPTION TYPE zcx_not_found MESSAGE ID 'ZDEMO' TYPE 'E' NUMBER '001' WITH id.
" Пробросити існуючий виняток (chaining)TRY. " ... CATCH cx_sy_open_sql_db INTO DATA(db_exc). RAISE EXCEPTION TYPE zcx_service_error EXPORTING previous = db_exc.ENDTRY.THROW — це RAISE EXCEPTION у формі виразу, зручно у COND/SWITCH.
TRY / CATCH
Section titled “TRY / CATCH”TRY. DATA(result) = service->load( id ). process( result ).
CATCH zcx_not_found INTO DATA(e_nf). log->warn( |Not found: { e_nf->id }| ). " fallback
CATCH zcx_service_error cx_sy_open_sql_db INTO DATA(e_other). log->error( e_other->get_text( ) ). RAISE EXCEPTION TYPE zcx_my_error EXPORTING previous = e_other.
CATCH BEFORE UNWIND cx_root INTO DATA(e_any). " catch-all на випадок чого завгодно log->fatal( e_any->get_text( ) ). RAISE. " прокинути як є
ENDTRY.Правила:
- Класи ловляться зверху вниз — перший збіг спрацьовує. Більш специфічні класи вище.
- Кілька класів у одному
CATCHчерез пробіл/кому. INTO e— отримати обʼєкт винятку.RAISE.без аргументів — перекинути поточний.
RESUMABLE — продовжити після виклику
Section titled “RESUMABLE — продовжити після виклику”Якщо виняток викинуто з RESUMABLE, обробник може зробити RESUME і продовжити з наступного оператора після RAISE:
" Виклик:RAISE RESUMABLE EXCEPTION TYPE zcx_warning.
" Обробник:CATCH RESUMABLE zcx_warning INTO DATA(w). log->warn( w->get_text( ) ). RESUME. " повернутись і продовжитиВикористовується рідко — у кастомних фреймворках. Зазвичай вистачає нормального flow без RESUME.
CLEANUP — еквівалент finally
Section titled “CLEANUP — еквівалент finally”Код, що виконається при виході з TRY через необроблений виняток:
TRY. open_file( ). process_file( ). CATCH zcx_parse_error. " ... CLEANUP. close_file( ). " виконається якщо виняток не піймався локальноENDTRY.RETRY у CATCH — повторює весь блок TRY. Використовується для ретраїв мережевих викликів:
DATA attempts TYPE i.
TRY. call_external_api( ). CATCH cx_http_timeout. attempts += 1. IF attempts < 3. WAIT UP TO 1 SECONDS. RETRY. ENDIF. RAISE.ENDTRY.Текст винятку
Section titled “Текст винятку”" Людино-читабельнийDATA(text) = e->get_text( ).
" Long text (якщо налаштовано у OTR)DATA(long) = e->get_longtext( ).
" ІдентифікаторDATA(source) = e->get_source_position( ).Повідомлення T100 як виняткові тексти
Section titled “Повідомлення T100 як виняткові тексти”Інтеграція з класичними Message Classes (SE91) — виняток несе MSGID/MSGNO/привʼязку змінних:
CLASS zcx_demo DEFINITION INHERITING FROM cx_static_check. PUBLIC SECTION. INTERFACES if_t100_dyn_msg. CONSTANTS: BEGIN OF invalid_id, msgid TYPE symsgid VALUE 'ZDEMO', msgno TYPE symsgno VALUE '010', attr1 TYPE scx_attrname VALUE 'ID', END OF invalid_id. DATA id TYPE string.ENDCLASS.
" Викинути зі змінною у повідомленніRAISE EXCEPTION TYPE zcx_demo MESSAGE ID 'ZDEMO' TYPE 'E' NUMBER '010' WITH id EXPORTING textid = zcx_demo=>invalid_id id = `X1`.Класичні винятки у function modules
Section titled “Класичні винятки у function modules”Старий стиль — через EXCEPTIONS:
CALL FUNCTION 'SOME_FM' EXPORTING input = x EXCEPTIONS not_found = 1 error = 2 OTHERS = 3.CASE sy-subrc. WHEN 1. " not_found WHEN 2. " errorENDCASE.Не використовуй у новому коді — загортай function modules у методи з class-based винятками.
Runtime errors і дампи
Section titled “Runtime errors і дампи”Runtime error (ABAP short dump) виникає, коли:
- Необроблений виняток
cx_no_checkабо не пійманеcx_dynamic_check. - Явне
RAISE SHORTDUMP TYPE .... - Порушення ABAP-контракту (ділення на нуль у цілих, розмір/тип не збігається, неприпустимий доступ до памʼяті).
Кидати short dump власноруч — вкрай рідко, лише там де продовжити неможливо:
RAISE SHORTDUMP TYPE zcx_fatal EXPORTING reason = `impossible state`.Assertions
Section titled “Assertions”" Якщо умова false — runtime errorASSERT x > 0.ASSERT id IS NOT INITIAL.ASSERT — для інваріантів, які ніколи не мають зламатися в нормальному коді. Це маркер “тут щось глибоко не так”. Не використовуй для валідації бізнес-вводу — там виняток.
Патерни
Section titled “Патерни”Wrap-and-rethrow
Section titled “Wrap-and-rethrow”Чужий низькорівневий виняток загортаємо у свій семантичний, зберігаючи ланцюг:
TRY. SELECT SINGLE * FROM ztab WHERE id = @id INTO @DATA(row). CATCH cx_sy_open_sql_db INTO DATA(db). RAISE EXCEPTION TYPE zcx_persistence_error EXPORTING previous = db.ENDTRY.Викликач бачить семантичний zcx_persistence_error, але через previous може дістатись до причини.
Validation fail-fast
Section titled “Validation fail-fast”METHOD save. IF customer-id IS INITIAL. RAISE EXCEPTION TYPE zcx_validation EXPORTING field = `id`. ENDIF. IF strlen( customer-email ) > 100. RAISE EXCEPTION TYPE zcx_validation EXPORTING field = `email`. ENDIF. " ...ENDMETHOD.Перетворення sy-subrc у виняток
Section titled “Перетворення sy-subrc у виняток”SELECT SINGLE * FROM ztab WHERE id = @id INTO @DATA(row).IF sy-subrc <> 0. RAISE EXCEPTION TYPE zcx_not_found EXPORTING id = id.ENDIF.Коротко
Section titled “Коротко”- Створюй свої класи-спадкоємці
cx_static_check. - Оголошуй
RAISINGу методах. - Ловиш конкретне, не
cx_rootбез причини. - Зберігай
previous— ланцюг врятує дебагера. ASSERT— для інваріантів; виняток — для бізнесу.
Адаптовано з 27_Exceptions.md (Apache 2.0). Повний перелік нюансів — в оригіналі.