式指向とは
手続型(Procedual)の構文(Statement)やサブルーチン、汎用モジュールの代わりに、式やメソッドを使ってコーディングする手法を、モダン ABAP では「式指向(Expression-Oriented)」と呼んでいる。
処理結果を返すコードのことを「式(Expression)」という。ABAP では、= を使って左辺に値が返るコードのことを指す。
例えば、足し算を行う処理を ABAP でコーディングする場合、
ADD variable1 TO variable2.
のように ADD ステートメントを使って記述する方法と、
variable2 = variable1 + variable2.
のように式を使って記述する方法がある。なお、式でデータを処理するための記号のことを「演算子(Operator)」という。上の例では、= や + が演算子になる。また、演算子によって処理される値や変数のことを「オペランド(Operand)」という。+ 演算子で処理される variable1 や variable2 がオペランドになる。
複雑な処理をコーディングする際には、式を使った方がより簡潔に記述できる。例えば、三角形の面積を計算する処理をステートメントでコードすると以下のようになる。
DATA: area TYPE ze_area.
PARAMETERS: base TYPE i OBLIGATORY,
height TYPE i OBLIGATORY.
START-OF-SELECTION.
MOVE base TO area.
MULTIPLY area BY height.
DIVIDE area BY 2.
計算結果を格納する変数を予め定義して、さらに乗算と除算を分けて記述しなければならないので、コードする量が多くなってしまう。しかし、式を使えば、以下のように簡潔にコードを記述できる。
PARAMETERS: base TYPE i OBLIGATORY,
height TYPE i OBLIGATORY.
START-OF-SELECTION.
DATA(area) = CONV ze_area( base * height / 2 ).
インライン宣言でのデータ型指定の項で説明したように、CONV 演算子は、( ) で処理した値を変換して = で左辺に結果を返すので、式指向のコーディングが可能である。
Returrning パラメーターを持ったクラスのメソッドも式指向でコーディングできる。
例えば、3 つの計算処理の結果を合計するコードを記述する場合、3 つの計算処理がサブルーチンで実装されていると、以下のようなコーディングが必要になる。
DATA: result TYPE i,
total TYPE i.
PARAMETERS: value1 TYPE i OBLIGATORY,
value2 TYPE i OBLIGATORY.
START-OF-SELECTION.
PERFORM subroutine1 USING value1 value2
CHANGING result.
ADD result TO total.
PERFORM subroutine2 USING value1 value2
CHANGING result.
ADD result TO total.
PERFORM subroutine3 USING value1 value2
CHANGING result.
ADD result TO total.
一方、クラスのメソッドで 3 つの計算処理が実装されていれば、各メソッドの Returning パラメーターで返された値を式で合計すればよいので、以下のように簡潔なコードで実装できる。
PARAMETERS: value1 TYPE i OBLIGATORY,
value2 TYPE i OBLIGATORY.
START-OF-SELECTION.
DATA(total) =
z_calculator=>method1( im_value1 = value1 im_value2 = value2 ) +
z_calculator=>method2( im_value1 = value1 im_value2 = value2 ) +
z_calculator=>method3( im_value1 = value1 im_value2 = value2 ).
このように、式指向のコーディングを行うことで、より簡潔で可読性の高いコードで複雑な処理を実装できる。
CONV 演算子
CONV は、別のデータ型に値を変換(Convert)するための演算子である。基本形は以下の通り。
CONV データ型( 変換元の値や変数、式等 )
CONV 演算子の後に半角スペースを挟んでデータ型を指定し、変換元の値や変数、式等を () で囲んで指定する。( の後と ) の前には半角スペースを挟む。なお、CONV の前に = を指定すると、左辺に変換した値が代入される。
例えば、変数 value1 の値を ABAP 事前定義データ型 i に変換して変数 value2 に代入する場合は、
value2 = CONV i( value1 ).
と記述する。代入先の変数のデータ型を ABAP コンパイラーが判定可能な場合は、データ型の代わりに # を記述すれば、コンパイラーが自動的に判定して値を自動変換してくれる。
例えば、以下のように代入先の変数 value2 のデータ型が TYPE で明示されている場合は、
DATA value2 TYPE i.
....
value2 = CONV #( value1 ).
と記述しても、ABAP 事前データ型 i に自動変換されて変数 value2 に値が代入される。
CONV 演算子を使った式指向コーディングで簡素にコードを記述できる。例えば、選択画面の販売単価、受注数量項目に入力された整数値をクラス z_salesorder のメソッド get_amount に引き渡して受注金額を計算する。従来のコーディングで記述すると以下のようになる。
DATA: netpr TYPE netpr, "販売単価
kwmeng TYPE kwmeng, "受注数量
netwr TYPE netwr. "受注金額
PARAMETERS: price TYPE i OBLIGATORY, "販売単価項目
quant TYPE i OBLIGATORY. "受注数量項目
....
MOVE price TO netpr. "販売単価項目の入力値をデータ型 netpr に変換
MOVE quant TO kwmeng. "受注金額項目の入力値をデータ型 kwmeng に変換
z_salesorder=>get_amount( EXPORTING im_netpr = netpr "変換してからメソッドに引き渡す
im_kwmeng = kwmeng
RECEIVING re_netwr = netwr ).
CONV 演算子を使って、式指向のコーディングで書き換えると、以下のように簡潔に記述できる。
PARAMETERS: price TYPE i OBLIGATORY,
quant TYPE i OBLIGATORY.
....
DATA(netwr) =
z_salesorder=>get_amount( im_netpr = CONV #( price )
im_kwmeng = CONV #( quant ) ).
なお、クラスメソッドでパラメーターのデータ型が明示的に指定されていれば、この例のように # で記述を省略できる。
VALUE 演算子
VALUE 演算子を使って、構造の各変数にまとめて値を代入できる。
従来の ABAP コーディングでは、以下のように変数ごとにステートメントを記述して値を代入する。
DATA profile TYPE zs_profile. "構造
....
MOVE '古田敦也' TO profile-name.
MOVE 182 TO profile-height.
MOVE 80 TO profile-weight.
VALUE 演算子を使うと、構造の各変数に簡易に値を代入できる。基本形は以下の通り。
VALUE 構造データ型( 変数名 = 初期値 .... )
VALUE 演算子の後に半角スペースを挟んで構造のデータ型を指定し、初期設定する値を () で囲んで指定する。( の後と ) の前には半角スペースを挟む。() の中では、変数名の後に半角スペースを挟んで =、その後に代入する値を指定する。なお、値を指定しなかった変数には、データ型ごとのデフォルト値が設定される。
例えば、VALUE 演算子を使って、上記のコードを書き換えると以下のようになる。
profile = VALUE zs_profile( name = '古田敦也' height = 182 weight = 80 ).
VALUE の後に構造 profile の構造データ型 zs_profile を指定し、構造 profile の変数 name、height、weight に値を設定している。なお、代入先の構造のデータ型が予め明示的に指定されていれば、VALUE の後の構造データ型の指定を # で省略できる。
DATA: profile TYPE zs_profile. "構造
....
profile = VALUE #( name = '古田敦也' height = 182 weight = 80 ).
さらに、VALUE 演算子を使って、内部テーブルにも値を代入できる。
従来のコーディングでは、
DATA: gds_profile TYPE zacas_profile_001, "構造
gdt_profile TYPE zacat_profile_001. "内部テーブル
....
MOVE '広沢克己' TO gds_profile-name.
MOVE 185 TO gds_profile-height.
MOVE 99 TO gds_profile-weight.
APPEND gds_profile TO gdt_profile.
MOVE '池山隆寛' TO gds_profile-name.
MOVE 183 TO gds_profile-height.
MOVE 75 TO gds_profile-weight.
APPEND gds_profile TO gdt_profile.
MOVE '古田敦也' TO gds_profile-name.
MOVE 182 TO gds_profile-height.
MOVE 80 TO gds_profile-weight.
APPEND gds_profile TO gdt_profile.
のように、1行ごとに構造に初期値を設定した後で、内部テーブルに APPEND する必要がある。
しかし、VALUE 演算子を使えば、少ないコードで簡易に記述できる。基本形は以下の通り。
VALUE テーブルデータ型( ( 変数名 = 初期値 .... ) ( 変数名 = 初期値 .... ) .... )
() のなかに、さらに行単位に () で囲んで変数ごとに値を指定する。構造を使って、行ごとに値を代入して内部テーブルに APPEND する必要はない。
例えば、上記のコードを書き換えると以下のようになる。
profiles =
VALUE zt_profile(
( name = '広沢克己' height = 185 weight = 99 )
( name = '池山隆寛' height = 183 weight = 75 )
( name = '古田敦也' height = 182 weight = 80 ) ).
VALUE の後に内部テーブル profiles のテーブルデータ型 zt_profile を指定し、3 人 = 3 行分のプロフィール情報を内部テーブルに初期設定している。
CORRESPONDING 演算子
CORRESPONDING は、構造間で変数の値を代入するための演算子である。MOVE-CORRESPONDING のように、同じ名前の変数同士で値が代入される。基本形は以下の通り。
CORRESPONDING 代入先の構造データ型( 代入元の構造 )
CORRESPONDING 演算子の後に半角スペースを挟んで代入先の構造データ型を指定し、代入元の構造名を () で囲んで指定する。( の後と ) の前には半角スペースを挟む。なお、代入先の構造のデータ型が明示的に指定されている場合は、# で構造データ型を省略できる。
以下のコードは、選手のプロフィール情報を管理する zs_profile、野球チームの情報を管理する typ_team、そして野球選手の情報を管理する zs_player の3つの構造データ型を定義している。
* プロフィール構造データ型
TYPES: BEGIN OF zs_profile,
name TYPE ze_name,
birthday TYPE ze_birthday,
height TYPE ze_height,
weight TYPE ze_weight,
uninumber TYPE ze_uninumber,
END OF zs_profile.
* 野球チーム構造データ型
TYPES: BEGIN OF zs_team,
city TYPE C LENGTH 15,
team TYPE c LENGTH 30,
END OF zs_team.
* 野球選手構造データ型
TYPES: BEGIN OF zs_player,
name TYPE ze_name,
birthday TYPE ze_birthday,
city TYPE c LENGTH 15,
team TYPE c LENGTH 30,
number TYPE ze_uninumber,
END OF zs_player.
次のコードは、データ型 zs_profile の構造 profile に大谷翔平のプロフィール情報を初期設定した後に、CORRESPONDING 演算子で構造 profile の値がデータ型 zs_player の構造 player に代入される。
* プロフィール構造に広沢克己のプロフィールを初期設定する。
DATA(profile) = VALUE zs_profile( name = '広沢克己'
birthday = '19620410'
height = 185
weight = 99
uninumber = 8 ).
* プロフィール構造の値を野球選手構造に代入する。
DATA(player) = CORRESPONDING zs_player( profile ).
* 野球選手情報を一覧画面に表示する。
WRITE: '氏名', player-name,
/ '生年月日', player-birthday,
/ '本拠地', player-city,
/ 'チーム', player-team,
/ '番号', player-number.
この例の場合、同じ名前の変数がある name(氏名)と birthday(生年月日)の値が、構造 profile から構造 player に代入されて一覧画面に表示される。

MAPPING オプションを使うと、名前の異なる変数同士で値を代入できる。基本形は以下の通り。
CORRESPONDING 代入先の構造データ型( 代入元の構造名 MAPPING 代入先の変数名 = 代入元の変数名 .... )
() のなかに指定した代入元の構造名の後に、前後に半角スペースを挟んで MAPPING、その後に代入先の変数名、前後に半角スペースを挟んで =、代入元の変数名を指定する。
例えば、プロフィール構造の変数 uninumber の値を野球選手構造の変数 number に代入する場合は、以下のように記述する。
DATA(player) = CORRESPONDING zs_player( profile MAPPING number = uninumber ).
構造 profile の変数 uninumber に初期設定された背番号 08 が、構造 player の変数 number に代入されて一覧画面に表示される。

なお、MOVE-CORRESPONDING ステートメントと異なり、CORRESPONDING 演算子を使用すると、値が代入されなかった変数は初期化される。
以下のコードでは、構造 profile からプロフィール情報を代入した後で、別の構造 team からチーム情報を構造 player に CORRESPONDING 演算子で代入している。
* プロフィール構造に大谷翔平のプロフィールを初期設定する。
DATA(profile) = VALUE zs_profile( name = '広沢克己'
birthday = '19620410'
height = 185
weight = 99
uninumber = 8 ).
* 野球チーム構造にヤクルトスワローズの情報を初期設定する。
DATA(team) = VALUE zs_team( city = '東京'
team = 'ヤクルトスワローズ' ).
* プロフィール構造の値を野球選手構造に代入する。
DATA(player) = CORRESPONDING zs_player( profile MAPPING number = uninumber ).
* 野球チーム構造の値を野球選手構造に代入する。
player = CORRESPONDING #( team ).
* 野球選手情報を一覧画面に表示する。
WRITE: '氏名', player-name,
/ '生年月日', player-birthday,
/ '本拠地', player-city,
/ 'チーム', player-team,
/ '番号', player-number.
このコードを有効化して実行すると、以下のような実行結果になる。

このように、野球チームの情報は代入されるが、先に代入したプロフィールの情報は初期化されてしまう。
MOVE-CORRESPONDING のように、値が代入されなかった変数を初期化しないようにするには、BASE オプションを使用する。基本形は以下の通り。
CORRESPONDING 代入先の構造データ型( BASE ( 代入先の構造名 ) 代入元の構造名 .... )
( の後に、前後に半角スペースを挟んで BASE、 (、半角スペース、コピー先の構造名、半角スペース、 ) 、半角スペース、その後に代入元の構造名を指定する。
例えば、構造 lds_profile から代入したプロフィール情報を保持したまま、構造 lds_term の情報を構造 lds_player に代入するには、以下のように記述する。
* プロフィール情報を保持したまま、野球チーム構造の値を野球選手構造に代入する。
player = CORRESPONDING #( BASE ( player ) team ).
このコードを実行すると、

このように、CORRESPONDING でチーム情報を代入しても、先に代入したプロフィール情報は初期化されない。
CORRESPONDING 演算子を使って、内部テーブルの値を別の内部テーブルに代入できる。
従来のコーディングでは、代入元の内部テーブルを LOOP 処理して、MOVE や MOVE-CORRESPONDING を使って1行ずつ値を構造に代入し、APPEND で代入先の内部テーブルに追加する必要がある。
例えば、プロフィール内部テーブル profiles から野球選手内部テーブル players に値を代入する処理を、従来のコーディングで記述すると以下のようになる。
DATA: profiles TYPE zt_profile, "プロフィール内部テーブル
players TYPE zt_player. "野球選手内部テーブル
DATA: profile TYPE zs_profile, "プロフィール構造
player TYPE zs_player. "野球選手構造
....
* コピー元の内部テーブルを LOOP 処理する。
LOOP AT profiles INTO profile.
* コピー元の構造からコピー先の構造に値をコピーする。
MOVE-CORRESPONDING profile TO player.
* 名前の異なる変数は、1つずつコピーする。
MOVE profile-uninumber TO player-number.
* 構造をコピー先の内部テーブルに追加する。
APPEND player TO players.
ENDLOOP.
CORRESPONDING 演算子を使えば、簡易なコードで記述できる。基本形は以下の通り。
CORRESPONDING 代入先のテーブルデータ型( 代入元の内部テーブル )
記述方法は、構造の代入と基本的に変わらない。なお、MAPPING や BASE オプションも使用できる。
例えば、上記の内部テーブル処理を CORRESPONDING 演算子で書き換えると、
players = CORRESPONDING #( profiles MAPPING number = uninumber ).
のようになる。
SWITCH 演算子
SWITCH は、条件を判断して値を返すための演算子である。従来の CASE ステートメントよりも簡潔に処理を記述できる。
以下のコードは、選択項目 land の値を CASE ステートメントで判断して処理を行っている。
DATA greeting TYPE string.
....
PARAMETERS land TYPE land1 OBLIGATORY.
....
CASE land.
WHEN 'JP'.
MOVE 'こんにちは。' TO greeting.
WHEN 'DE'.
MOVE 'Hallo.' TO greeting.
WHEN 'FR'.
MOVE 'Bonjour.' TO greeting.
WHEN OTHERS.
MOVE 'Hello.' TO greeting.
ENDCASE.
SWICH 演算子を使うと、簡潔にコードを記述できる。基本形は以下の通り。
SWITCH 値を返すデータ型( 判断対象の変数名 WHEN 条件値 THEN 返す値 .... ELSE デフォルト値 )
SWITCH の後に半角スペースを挟み、= で左辺に返す値のデータ型を指定し、その後に () で囲んで条件判断の処理を記述する。なお、( の後と ) の前には半角スペースを挟む。() のなかは、まず判断対象の変数名を指定し、半角スペースを前後に挟んで WHEN、条件値、半角スペースを前後に挟んで THEN、その後に返す値を指定する。これを条件ごとに繰り返して記述する。なお、いずれの条件にも合致しなかった場合に返す値は、ELSE の後に記述する。
例えば、上記の CASE ステートメントを SWITCH 演算子を使った式指向コーディングで書き換えると、以下のようになる。
DATA(greeting) = SWITCH string( land WHEN 'JP' THEN 'こんにちは。'
WHEN 'DE' THEN 'Hallo.'
WHEN 'FR' THEN 'Bonjour.'
ELSE 'Hello.' ).
この例では、選択項目 land の値を判断対象にして、国コードが JP の場合は 'こんにちは。'、DE の場合は 'Hallo.'、FR の場合は 'Bonjour.'、それ以外の場合は 'Hello.' の文字列を返している。なお、文字列は、SWITCH 演算子の後に指定されたデータ型 string に変換されて、= の左辺に指定された変数 greeting に代入される。
COND 演算子
COND も、条件を判断して値を返すための演算子である。複合した条件を判断して値を返す場合に使用する。
以下のコードは、height と weight の 2 つの値を判断して処理を行っている。CASE ステートメントでは、複合した条件を判断できないので、IF ステートメントを使って記述している。
DATA result TYPE string.
....
IF height >= 190 AND weight >= 100.
MOVE '身長 190 cm 以上、体重 100 kg 以上です。' TO result.
ELSEIF height >= 190 AND weight >= 90.
MOVE '身長 190 cm 以上、体重 90 kg 以上です。' TO result.
ELSEIF height >= 190.
MOVE '身長 190 cm 以上、体重 90 kg 未満です。' TO result.
ELSEIF weight >= 100.
MOVE '身長 190 cm 未満、体重 100 kg 以上です。' TO result.
ELSEIF weight >= 90.
MOVE '身長 190 cm 未満、体重 90 kg 以上です。' TO result.
ELSE.
MOVE '身長 190 cm 未満、体重 90 kg 未満です。' TO result.
ENDIF.
COND 演算子は、複合した条件を判断して値を返す。基本形は以下の通り。
COND 値を返すデータ型( WHEN 条件 THEN 返す値 .... ELSE デフォルト値 )
COND の後に半角スペースを挟み、= で左辺に返す値のデータ型を指定し、その後に () で囲んで条件判断の処理を記述する。なお、( の後と ) の前には半角スペースを挟む。() のなかは、 WHEN の後に半角スペースを挟んで条件を記述し、前後に半角スペースを挟んでTHEN、その後に返す値を指定する。これを条件ごとに繰り返して記述する。なお、いずれの条件にも合致しなかった場合に返す値は、ELSE の後に記述する。
例えば、上記の IF ステートメントを COND 演算子を使った式指向コーディングで書き換えると、以下のようになる。
DATA(result) =
COND string( WHEN height >= 190 AND weight >= 100 THEN
'身長 190 cm 以上、体重 100 kg 以上です。'
WHEN height >= 190 AND weight >= 90 THEN
'身長 190 cm 以上、体重 90 kg 以上です。'
WHEN height >= 190 THEN
'身長 190 cm 以上、体重 90 kg 未満です。'
WHEN weight >= 100 THEN
'身長 190 cm 未満、体重 100 kg 以上です。'
WHEN weight >= 90 THEN
'身長 190 cm 未満、体重 90 kg 以上です。'
ELSE '身長 190 cm 未満、体重 90 kg 未満です。' ).
COND 演算子を使えば、複雑な条件分岐の処理も可読性の高いコードで記述できる。
