Конструкторські вирази
Конструкторський вираз (constructor expression) — це оператор (VALUE, NEW, CONV, CAST, COND, SWITCH, FILTER, REDUCE і т.д.), що будує значення певного типу прямо у виразі. Замість кількох рядків з тимчасовими змінними отримуємо компактний one-liner, який компілятор перевіряє на типи.
Загальний шаблон: OPERATOR type( ... ) або OPERATOR #( ... ), де # означає “виведи тип з контексту” (target type inference).
Будує структури та внутрішні таблиці.
TYPES: BEGIN OF ty_row, id TYPE i, name TYPE string, END OF ty_row, ty_tab TYPE TABLE OF ty_row WITH EMPTY KEY.
" СтруктураDATA(row) = VALUE ty_row( id = 1 name = `Alice` ).
" Таблиця — кожен ( ... ) це рядокDATA(tab) = VALUE ty_tab( ( id = 1 name = `A` ) ( id = 2 name = `B` ) ).
" З # — тип береться з контексту (змінна вже оголошена)DATA tab2 TYPE ty_tab.tab2 = VALUE #( ( id = 3 name = `C` ) ).BASE — не перетирати
Section titled “BASE — не перетирати”Без BASE таблиця/структура повністю перезаписується:
tab = VALUE #( BASE tab ( id = 10 name = `X` ) ). " дописати до існуючогоrow = VALUE #( BASE row name = `new name` ). " змінити лише полеЗлиття кількох джерел
Section titled “Злиття кількох джерел”DATA(merged) = VALUE ty_tab( ( LINES OF tab1 ) ( LINES OF tab2 FROM 2 TO 5 ) ( id = 99 name = `manual` ) ).CORRESPONDING
Section titled “CORRESPONDING”Копіює структуру/таблицю у інший тип з автоматичним мапінгом за іменами полів:
TYPES: BEGIN OF ty_src, a TYPE i, b TYPE string, x TYPE c, END OF ty_src.TYPES: BEGIN OF ty_dst, a TYPE i, b TYPE string, y TYPE c, END OF ty_dst.
DATA src TYPE ty_src VALUE ( a = 1 b = `t` x = 'X' ).
DATA(dst) = CORRESPONDING ty_dst( src )." dst-a = 1, dst-b = 't', dst-y порожнє (нема відповідника x)Додатки:
" Зберегти існуючий вміст (за незбіжними полями)dst = CORRESPONDING #( BASE ( dst ) src ).
" Явний мапінг іменdst = CORRESPONDING #( src MAPPING y = x ).
" Виключити полеdst = CORRESPONDING #( src EXCEPT a ).
" Для таблиць — пропустити дублі за ключемdst_tab = CORRESPONDING #( src_tab DISCARDING DUPLICATES ).
" Глибокі структури — DEEP копіює вкладені компонентиdst = CORRESPONDING #( DEEP src ).Створює обʼєкт і повертає посилання на нього:
DATA(ref) = NEW zcl_service( dep = some_dep ).
" З # — тип з контекстуDATA ref2 TYPE REF TO zcl_service.ref2 = NEW #( dep = other_dep ).
" Анонімний data referenceDATA(dref) = NEW i( 42 ). " data ref на INT зі значенням 42Для обʼєктів — те саме, що CREATE OBJECT, тільки у форматі виразу.
Явне конвертування типу. Потрібне, коли компілятор сам не може вивести цільовий тип:
" Числовий літерал у decfloatDATA(d) = CONV decfloat34( '3.14' ).
" Запакувати у stringDATA(s) = CONV string( sy-datum ).Часто CONV використовується перед передачею у метод, який очікує конкретний тип.
Як CONV, але з жорсткою перевіркою: якщо значення не вміщається або втрачає точність — кидає виняток:
DATA(x) = EXACT i( '3' ). " OK → 3DATA(y) = EXACT i( '3.14' ). " дамп CX_SY_CONVERSION_ROUNDINGDATA(z) = EXACT c length 2( 'abc' ). " дамп — не вміщаєтьсяОтримати data reference на обʼєкт даних:
DATA num TYPE i VALUE 42.DATA(dref) = REF #( num ). " TYPE REF TO idref->* = 100. " змінює num
" REF на рядок таблиціDATA(row_ref) = REF #( itab[ 1 ] ).Downcast — звузити посилання до конкретного підтипу:
DATA animal TYPE REF TO lcl_animal.animal = NEW lcl_dog( ).
DATA(dog) = CAST lcl_dog( animal ). " тип REF TO lcl_dogdog->bark( ).Якщо реальний тип не сумісний — CX_SY_MOVE_CAST_ERROR. Захист — через IS INSTANCE OF.
Умовний вираз — як тернарний оператор, але з кількома WHEN:
DATA(label) = COND string( WHEN n < 0 THEN `від'ємне` WHEN n = 0 THEN `нуль` WHEN n > 10 THEN `велике` ELSE `мале` ).Без ELSE і без жодного співпадіння — результат має початкове значення типу (порожнє для string). Часта пастка, коли забули гілку.
SWITCH
Section titled “SWITCH”Те саме, що COND, але з єдиним операндом і літеральними варіантами:
DATA(day) = SWITCH string( sy-fdayw WHEN 1 THEN `Monday` WHEN 2 THEN `Tuesday` " ... ELSE `unknown` ).Коли гілок багато і всі порівнюють одну змінну — SWITCH читається краще за COND.
FILTER
Section titled “FILTER”Повертає підтаблицю за умовою — без копіювання усієї таблиці в памʼять:
DATA(big) = FILTER #( itab WHERE amount >= 1000 ).DATA(not_big) = FILTER #( itab EXCEPT WHERE amount >= 1000 ).
" З використанням secondary keyDATA(f) = FILTER #( itab USING KEY sec WHERE status = 'A' ).FILTER вимагає, щоб поля у WHERE відповідали або ключу таблиці, або щоб був вказаний USING KEY. Інакше синтаксична помилка.
Оголошує локальні змінні всередині виразу — зручно для уникнення повторних обчислень:
DATA(result) = VALUE ty_tab( LET base = get_base( ) tax = base * '0.20' IN ( total = base + tax type = `normal` ) ( total = base * 2 type = `double` ) ).LET ... IN ... доступне у всіх constructor-виразах.
FOR — ітерація у виразі
Section titled “FOR — ітерація у виразі”Трансформація однієї таблиці у іншу без явного LOOP:
" Проста трансформаціяDATA(names) = VALUE string_table( FOR line IN users ( line-name ) ).
" З WHERE — тільки потрібні рядкиDATA(actives) = VALUE ty_tab( FOR line IN users WHERE ( status = 'A' ) ( id = line-id name = line-name ) ).
" FROM ... UNTIL — діапазон індексівDATA(squares) = VALUE ty_nums( FOR i = 1 UNTIL i > 10 ( n = i * i ) ).
" Кілька джерелDATA(pairs) = VALUE ty_pairs( FOR a IN list_a FOR b IN list_b ( first = a second = b ) ).REDUCE
Section titled “REDUCE”Згортка — обчислення скалярного значення з таблиці (сума, мінімум, агрегат):
" СумаDATA(total) = REDUCE i( INIT s = 0 FOR line IN itab NEXT s = s + line-amount ).
" МаксимумDATA(mx) = REDUCE i( INIT m = 0 FOR line IN itab NEXT m = COND #( WHEN line-v > m THEN line-v ELSE m ) ).
" КонкатенаціяDATA(csv) = REDUCE string( INIT r = `` FOR line IN itab NEXT r = COND #( WHEN r IS INITIAL THEN line-name ELSE |{ r },{ line-name }| ) ).INIT задає стартове значення, NEXT — як оновлюється акумулятор на кожному кроці.
Групування через VALUE/REDUCE
Section titled “Групування через VALUE/REDUCE”Групування (GROUP BY) всередині виразу — читаємо як SELECT з угрупуванням:
DATA(by_dept) = VALUE ty_grp( FOR GROUPS <grp> OF line IN employees GROUP BY line-dept ( dept = <grp> count = REDUCE i( INIT c = 0 FOR m IN GROUP <grp> NEXT c = c + 1 ) total = REDUCE p( INIT s = 0 FOR m IN GROUP <grp> NEXT s = s + m-salary ) ) ).Детальніше — див. сторінку про групування внутрішніх таблиць.
Комбіновані приклади
Section titled “Комбіновані приклади”" Таблиця активних користувачів з нормалізованими іменамиDATA(users) = VALUE ty_users( FOR u IN raw_users WHERE ( active = abap_true ) ( id = u-id name = to_upper( u-name ) created_at = CONV timestampl( u-ts ) ) ).
" Безпечний пошук зі значенням за замовчуваннямDATA(user) = VALUE #( users[ id = 42 ] DEFAULT VALUE #( id = 0 name = `unknown` ) ).Коли який оператор
Section titled “Коли який оператор”| Задача | Оператор |
|---|---|
| Побудувати структуру/таблицю | VALUE |
| Скопіювати з мапінгом за іменами | CORRESPONDING |
| Створити обʼєкт або data reference | NEW |
| Конвертація типу | CONV (м’яка), EXACT (строга) |
| Посилання на обʼєкт даних | REF |
| Downcast | CAST |
| Кілька умов → значення | COND |
| Одна змінна → варіант | SWITCH |
| Фільтрувати таблицю | FILTER |
| Трансформувати таблицю | VALUE ... FOR |
| Звернути таблицю у скаляр | REDUCE |
Адаптовано з 05_Constructor_Expressions.md (Apache 2.0). Повний перелік нюансів — в оригіналі.