Начиная с ABAP 7.4. нам стали доступны операторы конструкторы, такие как:
1 2 3 4 |
... NEW| VALUE| CONV| REDUCE| CAST| REF| EXACT| CORRESPONDING| COND| SWITCH | FILTER| type( ... ) ... |
Одной из особенностей данных операторов является возможность неявного определения типа через #, из-за чего у некоторых разработчиков часто возникают ошибки связанные с их использованием.
Далее рассмотрим типичные из них.
Возьмём оператор COND (релевантно и для SWITCH):
1 2 |
DATA(lv_val) = COND #( WHEN 1 = 2 THEN space ELSE 777 ). |
Результатом данного выражения будет — *.
If the operand type is not fully identifiable, an operand with a statically identifiable type must be specified after the first THEN (except when passing the constructor parameter to an actual parameter with generically typed formal parameter). This type is then used. In particular, THROWs cannot then be specified after THEN.
А все из-за того что, тип будет выведен неявно из первого условия после THEN, т.е. будет соответствовать константе space. Константа space в ABAP объявлена с типом C длиной 1 символ. Условие 1 = 2 очевидно не будет выполнено и переменной lv_val с типом C и длиной 1 символ будет присвоено значение 777. А согласно правилам конвертации, если при преобразовании числа к символьному типу оно не влезает по размеру, результат будет заменён * (некоторые говорят в таких случаях «SAP celebrates christmas»).
Другой частой ошибкой является использование оператора REDUCE #:
1 2 3 4 5 6 7 8 9 10 11 |
DATA: lt_values TYPE STANDARD TABLE OF decfloat34, lv_sum TYPE decfloat34. lt_values = VALUE #( ( CONV #( '0.1' ) ) ( CONV #( '0.2' ) ) ( CONV #( '0.3' ) ) ). lv_sum = REDUCE #( INIT sum = 0 FOR <lv> IN lt_values NEXT sum = sum + <lv> ). |
Результат выражения будет — 0.
Тип временной переменной sum будет выведен исходя из присвоения ( = 0) — в данном случае integer. REDUCE так же возвратит integer исходя из типа переменной sum.
Но даже если явным образом объявить тип переменной после REDUCE, результат будет тот же:
1 2 3 |
DATA(lv_sum) = REDUCE decfloat34( INIT sum = 0 FOR <lv> IN lt_values NEXT sum = sum + <lv> ). |
Результат остаётся прежним, т.к. переменная sum все еще имеет тип integer, соответственно при суммировании срабатывает целочисленная арифметика, после чего результат целогочисленного суммирования в поле sum присваивается к результату с типом decfloat34.
Чтобы корректно посчитать сумму, следует явно указать тип, в рамках которого будут выполняться расчёты:
1 2 3 |
DATA(lv_sum) = REDUCE #( INIT sum TYPE decfloat34 FOR <lv> IN lt_values NEXT sum = sum + <lv> ). |
А еще лучше указывать тип и там и там:
1 2 3 |
DATA(lv_sum) = REDUCE decfloat34( INIT sum TYPE decfloat34 FOR <lv> IN lt_values NEXT sum = sum + <lv> ). |
В целом общая рекомендация что по данным примерам, что в целом: если что-то можно сделать явным образом — лучше сделать это явно, чем полагаться на неявное поведение выполняемое системой за вас 🙂
Вот и я обнаружил это на своём личном опыте
cond #( when s_vkorg is initial then ‘VK’ else conv string( s_vkorg[ 1 ]-low ) ) возвращает вместо 2156 «21»