コードの分析およびテスト
データ型およびデータ型変換の正しい使用
文字項目の処理
ABAP SQL でのコードプッシュダウンの使用
内部テーブルのパフォーマンスの改善
権限チェックの実装
効果的なオブジェクト指向コードの設計
例外クラスの定義と処理
ABAP コードへの文書の追加

データ型変換の落とし穴の回避

Objective

After completing this lesson, you will be able to データ型変換の落とし穴を回避します。

成功した割当

ABAP では、異なるデータ型の変数間で値割当を実行することができます。これを行うと、ABAP によってソース項目の値がターゲット項目のデータ型に変換されます。変換が成功するかどうかは、ソース項目の値がターゲット項目のデータ型と互換性があるかどうかによって異なります。

ここでの最初の例では、文字列の値を整数フィールドに割り当てようとしています。文字列 12345 の値は有効な整数であるため、割当は成功します。2 番目の割当も成功します。

試してみる - 成功した割当

  1. インタフェース IF_OO_ADT_CLASSRUN を実装する新しいクラスを登録します。
  2. 以下のコードを METHOD if_oo_adt_classrun~main. 文と ENDMETHOD. 文の間でエディタにコピーします。
    Code Snippet
    1234567891011121314151617181920
    DATA var_string TYPE string. DATA var_int TYPE i. DATA var_date TYPE d. data var_pack type p length 3 decimals 2. var_string = `12345`. var_int = var_string. out->write( 'Conversion successful' ). var_string = `20230101`. var_date = var_string. out->write( |String value: { var_string }| ). out->write( |Date Value: { var_date date = user }| ).
  3. Ctrl + F3 を押してクラスを有効化します。
  4. コードを試して、トピックについて理解します。

失敗した割当

値割当でのデータ型変換は、常に成功するわけではありません。ソース値がターゲット項目のデータ型と互換性がない場合があります。ここに表示される 2 つの割当の両方によって例外が発生し、捕捉されないと実行時エラーが発生します。

文字列値 ABCDE を整数項目に割り当てようとすると、文字列の内容を数値として表すことができないことが認識され、例外 CX_SY_CONVERSION_NO_NUMBER がトリガされます。2 番目の例では、長さ 3 と小数点以下桁数 2 のパック数値に値 1000 を割り当てようとしています。したがって、変数は 5 桁で、そのうち 2 桁は小数点以下桁数であるため、最大値は 999.99 です。そのため、この割当によって算術オーバーフローが発生し、システムが例外 CX_SY_CONVERSION_OVERFLOW で応答します。

切り捨ておよび丸め

一部の割り当ては、データの損失や精度の低下につながります。たとえば、文字項目の値をより短い項目に割り当てると、その値は切り詰められます。この例では、長さが 3 の文字項目に 5 文字を割り当てようとしました。この場合、4 番目と 5 番目の文字は失われます。

数値項目に値を割り当て、ターゲット項目の小数点以下桁数が不十分な場合は、算術丸めが使用されます。この 2 つの例では、1 つの四半期を 0.25 と正確に表すことができますが、8 分の 1 - 0.125 - は 0.13 に切り上げられます。

試してみる - 切り捨ておよび丸め

  1. インタフェース IF_OO_ADT_CLASSRUN を実装する新しいクラスを登録します。
  2. 以下のコードを METHOD if_oo_adt_classrun~main. 文と ENDMETHOD. 文の間でエディタにコピーします。
    Code Snippet
    1234567891011121314151617181920
    DATA long_char TYPE c LENGTH 10. DATA short_char TYPE c LENGTH 5. DATA result TYPE p LENGTH 3 DECIMALS 2. long_char = 'ABCDEFGHIJ'. short_char = long_char. out->write( long_char ). out->write( short_char ). result = 1 / 8. out->write( |1 / 8 is rounded to { result NUMBER = USER }| ).
  3. Ctrl + F3 を押してクラスを有効化します。
  4. コードを試して、トピックについて理解します。

アサインメントの予期しない結果

2 つの異なるデータ型間の割当の結果が予期せぬ結果になる場合があります。たとえば、日付項目を整数項目に割り当てると、値は 0001/01/01 以降の日数になります。同様に、時間項目を整数に割り当てると、結果は午前 0 時からの秒数になります。

文字項目または文字列をデータ型 N の変数に割り当てると、数字以外の文字が破棄され、項目の残りの桁が右寄せで配置され、残りのスペースに先行ゼロが入力されます。

日付項目は、技術的な観点から見ると文字項目です。そのため、有効な日付に対応しない ABAP の日付項目に値を割り当てることができます (UI レイヤによって日付がチェックされるため、ユーザが UI を使用して日付を入力した場合は、これは行われないことに注意してください)。

インライン宣言の落とし穴

算術式でインライン宣言を使用すると、式の右側から新しい変数の型が導出されます。数値リテラル 5 および 10 は整数であるため、変数 result1 および result2 も整数として登録されます。乗算の結果は正しいですが、除算の結果は 1 に切り上げられます。

試してみる - 割り当ての予期しない結果

  1. インタフェース IF_OO_ADT_CLASSRUN を実装する新しいクラスを登録します。
  2. 以下のコードを METHOD if_oo_adt_classrun~main. 文と ENDMETHOD. 文の間でエディタにコピーします。
    Code Snippet
    123456789101112131415161718192021
    DATA var_date TYPE d. DATA var_int TYPE i. DATA var_string TYPE string. DATA var_n TYPE n LENGTH 4. var_date = cl_abap_context_info=>get_system_date( ). var_int = var_date. out->write( |Date as date| ). out->write( var_date ). out->write( |Date assigned to integer| ). out->write( var_int ). var_string = `R2D2`. var_n = var_string. out->write( |String| ). out->write( var_string ). out->write( |String assigned to type N| ). out->write( var_n ).
  3. Ctrl + F3 を押してクラスを有効化します。
  4. コードを試して、トピックについて理解します。

強制タイプの変換

データ型変換を明示的にまたは暗黙的に強制する方法については、このビデオを視聴してください。

試してみる - 強制タイプの変換

  1. インタフェース IF_OO_ADT_CLASSRUN を実装する新しいクラスを登録します。
  2. 以下のコードを METHOD if_oo_adt_classrun~main. 文と ENDMETHOD. 文の間でエディタにコピーします。
    Code Snippet
    1234567891011121314151617181920212223
    * result has type C. * and is displayed unformatted in the console DATA(result1) = '20230101'. out->write( result1 ). * result2 is forced to have type D * and is displayed with date formatting in the console DATA(result2) = CONV d( '20230101' ). out->write( result2 ). * The method do_something( ) has an importing parameter of type string. * Attempting to pass var results in a syntax error * The CONV #( ) expression converts var into the expected type * Note that CONV #( ) can lead to conversion exceptions * lcl_class=>do_something( var ).
  3. Ctrl + F3 を押してクラスを有効化します。
  4. コードを試して、トピックについて理解します。

切り捨ておよび丸めの防止

切り詰めや丸めを防ぐ方法については、このビデオを視聴してください。

試してみる - 切り捨ておよび丸めの防止

  1. インタフェース IF_OO_ADT_CLASSRUN を実装する新しいクラスを登録します。
  2. 以下のコードを METHOD if_oo_adt_classrun~main. 文と ENDMETHOD. 文の間でエディタにコピーします。
    Code Snippet
    12345678910111213141516171819202122232425262728293031323334353637383940414243444546
    DATA var_date TYPE d. DATA var_pack TYPE p LENGTH 3 DECIMALS 2. DATA var_string TYPE string. DATA var_char TYPE c LENGTH 3. var_pack = 1 / 8. out->write( |1/8 = { var_pack NUMBER = USER }| ). TRY. var_pack = EXACT #( 1 / 8 ). CATCH cx_sy_conversion_error. out->write( |1/8 has to be rounded. EXACT triggered an exception| ). ENDTRY. var_string = 'ABCDE'. var_char = var_string. out->write( var_char ). TRY. var_char = EXACT #( var_string ). CATCH cx_sy_conversion_error. out->write( 'String has to be truncated. EXACT triggered an exception' ). ENDTRY. var_date = 'ABCDEFGH'. out->write( var_Date ). TRY. var_date = EXACT #( 'ABCDEFGH' ). CATCH cx_sy_conversion_error. out->write( |ABCDEFGH is not a valid date. EXACT triggered an exception| ). ENDTRY. var_date = '20221232'. out->write( var_date ). TRY. var_date = EXACT #( '20221232' ). CATCH cx_sy_conversion_error. out->write( |2022-12-32 is not a valid date. EXACT triggered an exception| ). ENDTRY.
  3. Ctrl + F3 を押してクラスを有効化します。
  4. コードを試して、トピックについて理解します。

問題のあるデータ型変換の回避

ABAP クラスのコンソール出力を詳しく確認し、航空会社名が切り詰められていることを確認します。また、貨物フライトに対して表示される運送業者 ID および接続 ID に問題があることにも気付きました。問題のあるデータ型変換をこれらの問題の原因として特定し、それに応じてコードを修正します。

テンプレート:

  • /LRN/CL_S4D401_ATS_SQL_TRACE (グローバルクラス)

ソリューション:

  • /LRN/CL_S4D401_TCS_TYPE_CONV (グローバルクラス)

タスク 1: テンプレートのコピー (オプション)

テンプレートクラス /LRN/CL_S4D401_ATS_SQL_TRACE をコピーします。前の演習問題を完了した場合は、このタスクをスキップし、クラス ZCL_##_SOLUTION の編集を続行することができます。

ステップ

  1. クラス /LRN/CL_S4D401_ATS_SQL_TRACE を独自のパッケージのクラスにコピーします (推奨名: ZCL_##_SOLUTION、## はグループ番号)。

    1. Project Explorer で、クラス /LRN/CL_S4D401_ATS_SQL_TRACE を右クリックしてコンテキストメニューを開きます。

    2. コンテキストメニューから Duplicate.... を選択します。

    3. Package 項目にパッケージの名称を入力します。Name 項目に、ZCL_##_SOLUTION (## はグループ番号) と入力します。

    4. Next を選択します。

    5. 移送依頼を確認し、Finish を選択します。

  2. コピーを有効化します。

    1. Ctrl + F3 を押してクラスを有効化します。

タスク 2: 問題のあるデータ型変換の検出

クラスをコンソールアプリとして実行します。コンソール出力を検査し、貨物フライトの運送業者名、運送業者 ID、および接続 ID に特に注意してください。この出力を生成するコードを見つけ、潜在的な問題のあるデータ型変換に注意しながら、データをソースまでトレースします。EXACT 式を使用して、データ損失のあるデータ型変換があることを確認します。

ステップ

  1. ABAP クラス ZCL_##_SOLUTION をコンソールアプリとして実行します。

    1. エディタでクラスを開き、F9 を押します。

  2. コンソール出力を確認します。

    1. Console ビューで下にスクロールします。

    2. Carrier Name: で始まる行を見つけます。

    3. Found a correct cargo flight で始まる行まで下にスクロールし、次の行を検査します。

  3. ABAP クラスのソースコードに戻り、メソッド if_oo_adt_classrun~main の実装を分析します。

  4. メソッド get_output にナビゲートします。

    1. メソッドコール carrier->get_output( ) で、カーソルをメソッド名 get_output に置き、F3 を押します。または、Ctrl キーを押しながらメソッド名をクリックすることもできます。

    2. me->name で、名称にカーソルを置き、F2 を押して属性名のコードエレメント情報を表示します。

    3. r_result にカーソルを置き、F2 を押して get_output メソッドのリターンパラメータ r_result のコードエレメント情報を表示します。

    4. コードエレメント情報で、tt_output をクリックして、このデータ型のコードエレメント情報を表示します。

    5. t_output をクリックして、tt_output テーブルデータ型の行データ型のコードエレメント情報にナビゲートします。

  5. パラメータ r_result の行データ型を変更します。可変長の文字型行データ型を使用します。

    1. 要素情報の下部にあるツールバーから、エディタで開くを選択します。または、F3 を押して、タイプ t_output の定義にナビゲートすることができます。

    2. TYPES t_output TYPE c LENGTH 40. で、c LENGTH 40string に置き換えます。

  6. Global Class タブに戻り、クラス lcl_cargo_flight のメソッド get_description にナビゲートします。

    1. メソッドコール cargo_flight->get_description( ) で、メソッド名 get_description にカーソルを置き、F3 を押します。または、Ctrl キーを押しながらメソッド名をクリックすることもできます。

  7. 属性 carrier_idの使用先一覧を使用して、その属性が入力されている命令を検索します。

    1. carrier_id にカーソルを置き、Eclipse ツールバーから使用先一覧取得を選択します。または、Ctrl + Shift + G を押すこともできます。

    2. エディタの下の検索ビューで、carrier_id = で始まる行をダブルクリックします。

    3. carrier_id = i_carrier_id. で、i_carrier_id にカーソルを置き、F2 を押して i_carrier_id のコードエレメント情報を表示します。

    4. コードエレメント情報で、 /dmo/carrier_id をクリックして、対応するコードエレメント情報にナビゲートします。/dmo/carrier_id の型は char(3) です。

    5. これを適宜繰り返して、carrier_id の技術タイプを分析します。ここには、データ型 numc(4) が表示されます。

  8. データ型変換によって情報が失われるかどうかをテストするには、変換式 EXACT を使用します。コードを有効化し、コンソールアプリとして実行します。

    1. 特性割当では、以下のように、ソースデータオブジェクト i_carrier_idEXACT #( ... ) で囲みます。

      ABAP
      1
      carrier_id = EXACT #( i_carrier_id ).
    2. Ctrl + F3 を押してクラスを有効化します。

    3. F9 を押して、クラスをコンソールアプリとして実行します。

    4. 実行時エラー CONVT_NO_NUMBER が表示されます。

  9. 割当 connection_id = i_connection_id に対してタイプ分析を繰り返します。

    1. 前のステップと同様に、connection_id および i_connection_id の技術タイプを分析します。

  10. 変換式 EXACT を使用して、割当 connection_id = i_connection_id のタイプ変換によって情報が失われるかどうかをテストします。

    1. コードを以下のように調整します。

      ABAP
      1
      connection_id = EXACT #( i_connection_id ).
    2. この場合、構文チェックでは、損失のない割当を実行できないことがすでに検出されています。エラー "I_CONNECTION_ID" のタイプ "/DMO/CONNECTION_ID" は、"ME->CONNECTION_ID" のタイプ "/DMO/CARRIER_ID" と互換性がありません

  11. 属性 carrier_id および connection_id のタイプを修正し、2 つの EXACT 式を削除します。その後、コードを有効化して再テストします。

    1. carrier_id = EXACT #( i_carrier_id ). で、carrier_id にカーソルを置き、F3 を押してその属性の定義にナビゲートします。または、Ctrl キーを押しながら属性名をクリックすることもできます。

    2. DATA carrier_id TYPE /dmo/connection_id READ-ONLY. で、/dmo/connection_id/dmo/carrier_id に置き換えます。

    3. DATA connection_id TYPE /dmo/carrier_id READ-ONLY. で、/dmo/carrier_id/dmo/connection_id に置き換えます。

    4. 属性の宣言は以下のようになります。

      ABAP
      123456
      * wrong: * DATA carrier_id TYPE /dmo/connection_id READ-ONLY. * DATA connection_id TYPE /dmo/carrier_id READ-ONLY. * correct: DATA carrier_id TYPE /dmo/carrier_id READ-ONLY. DATA connection_id TYPE /dmo/connection_id READ-ONLY.
    5. 以前に追加した EXACT 式を削除することで、構文の警告 [型... の冗長変換] を修正します。

    6. Ctrl + F3 を押してクラスを有効化します。

    7. F9 を押して、クラスをコンソールアプリとして実行します。