拡張メソッドを使用すると、メソッドを既存のタイプに追加できるため、コードの編集や再コンパイルを行わずに追加機能が提供されます。
C# などの他のマネージ コード言語と同様に、マネージ COBOL は拡張メソッドをサポートします。これにより、既存のタイプにメソッドを追加できるため、コードの編集や再コンパイルを行わずに追加機能を提供できます。
例えば、次の拡張メソッドは、文字列の単語数を数えるメソッドを追加することで、文字列クラスを拡張します。
class-id MyCount static. method-id CountWords extension. *> 拡張メソッドは暗黙的に静的 procedure division using str as string returning wordCount as binary-long. set wordCount to str::Split(' ')::Length end method. end class.
メソッドをオーバーライドできない場合は拡張します。拡張するメソッドが、アクセスできない最終クラスまたはコードにある場合があります。このような場合、拡張メソッドを個別に定義してコンパイルし、そのメソッドを自由に使用できます。
次の構成を使用して、拡張メソッドを定義します。
class-id MyExtension static. method-id MyExtensionMethod extension. procedure division using myName as ExtendedType returning myReturnName as returnType. set myReturnName to my code... end method. end class.
拡張メソッドを定義するには、次のようにします。
拡張メソッドを呼び出すための特別な構文はありません。拡張メソッドは他のメソッドとほぼ同じように使用します。ただし、最初のパラメーターを、拡張されるタイプとして指定する点を除きます。次の構文を使用します。
parameter-1::method-name(more parameters)
次に示す例では、次のようなコードで使用します。 aString::CountWords:
class-id MyWords. method-id CheckWords. 01 myWordCount binary-long. procedure division using value aString as string returning b as condition-value. set myWordCount to aString::CountWords if myWordCount < 10 set b to true else set b to false end-if end method. end class.
拡張メソッドは、別のクラスやインターフェイスで定義されるインスタンス メソッドであるかのように使用します。拡張メソッドは、オブジェクト インスタンスで利用可能な追加メソッドとして表示されますが、どの場所にも実装されます。
拡張メソッドを呼び出す場合、そのメソッドは、呼び出し文で指定されるクラスには含まれません。メソッドは別のクラスのどこかに含まれます。呼び出しクラスと実行クラスは異なります。メソッドの定義される包含タイプと実行する包含タイプは異なります。
拡張メソッドは次のように機能します。
拡張演算子は、拡張メソッドとかなり似ています。拡張演算子を使用すると、コードの編集や再コンパイルを行わずに、演算子の代替動作を提供できます。拡張演算子は、拡張メソッドと同様に定義して使用します。
例えば、拡張と演算子を次のように定義して使用できます。
set timer3 to timer1 + myMins
operator-id + extension. 01 sumMins binary-long. 01 m binary-long value 0. 01 h binary-long value 0. procedure division using value a as type Timer b as binary-long returning c as type Timer. set sumMins to a::pMins + b divide sumMins by 60 giving h remainder m set c to new Timer(a::pHour + h , m) end operator.
演算子をオーバーロードできない場合は拡張します。拡張する演算子が、アクセスできない最終クラスまたはコードにある場合があります。このような場合、拡張演算子を個別に定義してコンパイルし、その演算子を自由に使用できます。
拡張演算子を宣言して使用するには、次のようにします。
拡張演算子は、op_Equality や op_Addition などのメソッドとして実装されます。このメソッドは、TypeInfo の固有辞書に追加されます。このため、演算子のオーバーロードと拡張を検索するコードは、2 つのオペランドのクラスの他にこの辞書も調べることができます。
メソッドを呼び出す場合、そのメソッドが属するクラスを指定します。次の例では、myWord クラスのメソッド CheckWords() は次のように呼び出されます。
01 myWord type MyWords. 01 myDoc string. 01 sizeOK condition-value. set sizeOK to myWord::CheckWords(myDoc) ... class-id MyWords final. method-id CheckWords. ...
上記の例では、クラスが見つかるため、メソッドも見つかります。クラス ローダーのアーキテクチャは、メソッドが定義されるクラスを使用してクラスのロード時期を把握します。この場合、myWord クラスは、メソッド CheckWords() を呼び出す前にロードされます。これは、コンパイル時にほぼ同様に行われます。myWord::CheckWords メソッドが、コンパイルされるクラスで参照される場合、コンパイラは CheckWords メソッドをロードするクラス ローダーを使用して、メソッドの署名を解決し、適切なコードの生成方法を把握します。
ただし、拡張メソッド (または演算子) を呼び出す場合、拡張メソッドは、呼び出し文で指定されるクラスには含まれません。次の例では、拡張メソッド CountWords() は、aString 変数 (文字列) のタイプで呼び出されますが、次のように拡張クラスで実行されます。
01 aString string. 01 myWordCount binary-long. set myWordCount to aString::CountWords ... class-id MyCount static. method-id CountWords extension. ...
メソッドは String クラスで呼び出されますが、実行されるメソッドは、別のクラス (MyCount クラス) に含まれます。クラス ローダーはメソッドを見つけることができません。その定義される包含タイプと実行する包含タイプが異なるためです。コンパイラが拡張メソッド (および拡張演算子) を使用するには、その拡張を参照するクラスをコンパイルする前に定義クラスをロードする必要があります。
コンパイラが拡張を見つけられるようにするには、コマンド ラインで ILREF 指令を JVMGEN 指令と組み合わせて使用します。次の例では、ops.class ファイル内に定義されるクラスをロードします。ops.class で定義されるクラスの名前とパッケージは、クラス ファイルの名前や辞書と同じである必要はありません。
Windows: |
cobol x.cbl jvmgen ilref(ops.class); |
UNIX: |
cob x.cbl –C jvmgen -C ilref(ops.class) |
次のコマンドは、ファイル ops.jar を解凍して moreOps.class をロードします。.jar ファイルが機能するには、CLASSPATH 環境変数上にあるか、JVMCLASSPATH 指令で指定される必要があります。
Windows: |
cobol -j x.cbl jvmclasspath(ops.jar) ilref(moreOps.class); |
UNIX: |
cob -j x.cbl -C jvmclasspath(ops.jar) –C ilref(moreOps.class) |
ILREF 指令には、クラス フォーマットのファイルが必要です。.class 拡張のあるファイルはクラス フォーマットで扱われます。
JVM の場合、次の拡張がサポートされます。
拡張を含むクラスは、ILREF 指令を使用することにより、コンパイラであらかじめロードされます。
文字列同一の拡張演算子は、コンパイルの前にコンパイラであらかじめロードされます。これにより、同一であるかどうかのチェックが行われ、.NET 環境の結果と JVM 環境の結果が同じになります。例えば、次のコードは 2 つの文字列を比較します。
01 string1 string value “12345”. 01 string2 string value “12345”. if string1 = string2 display “the values are the same” else display “error - the values look the same, but the equality returned false” end-if
あらかじめロードされた拡張を使用しない場合、コードは文字列を異なるものと判断します。これは、JVM java.lang.String::equality(string) メソッドが参照を比較し、.NET System.String::equality(string) メソッドが値を比較するためです。あらかじめロードされた拡張を使用する場合、コードは文字列を同じものと判断します。
文字列同一の拡張は、文字列タイプの場合、= と等号演算子の両方を拡張します。演算子は、2 つの文字列インスタンスに同じ文字シーケンスが正確に含まれる場合は true を戻し、それ以外は false を戻します。演算子の拡張は次のように定義されます。
operator-id = extension. procedure division using value a as string b as string returning r as condition-value.
文字列インスタンス間でオブジェクトが同一であるかどうかを確認するには、次の構文を使用します。
if a as object = b as object
従属文字列の拡張メソッドは、コンパイルの前にコンパイラであらかじめロードされます。これにより、従属文字列メソッドでは、.NET 環境の結果と JVM 環境の結果が同じになります。次に例を示します。
01 x string value “1234567890”. display x::Substring(2,3) “ The .NET way of doing things” display x::substring(2,3) “ The JDK way of doing things”
System.String::Substring(int, int) の .NET 動作は java.lang.String::Substring(int int) の JVM 動作と異なるため、拡張がインストールされない場合、.NET と JVM の結果は異なります。ただし、拡張がインストールされる場合、JVM 動作は .NET の動作と一致し、同じ結果になります。
従属文字列の拡張メソッドは、文字列タイプ (java.lang.String でもある) を拡張します。このメソッドは、.NET の従属文字列のメソッドの署名と一致する署名の文字列の新しいインスタンスを戻します。従属文字列のメソッドの拡張は次のように定義されます。
method-id Substring extension. procedure division using value a as string startIdx as binary-long sslength as binary-long returning r as string.
文字列同一の拡張演算子は、コンパイルの前にコンパイラであらかじめロードされます。これにより、同一であるかどうかのチェックが行われ、.NET 環境の結果と JVM 環境の結果が同じになります。例えば、次のコードは 2 つの文字列を比較します。
01 string1 string value “12345”. 01 string2 string value “12345”. if string1 = string2 display “the values are the same” else display “error - the values look the same, but the equality returned false” end-if
あらかじめロードされた拡張を使用しない場合、コードは文字列を異なるものと判断します。これは、JVM java.lang.String::equality(string) メソッドが参照を比較し、.NET System.String::equality(string) メソッドが値を比較するためです。あらかじめロードされた拡張を使用する場合、コードは文字列を同じものと判断します。
文字列同一の拡張は、文字列タイプの場合、= と等号演算子の両方を拡張します。演算子は、2 つの文字列インスタンスに同じ文字シーケンスが正確に含まれる場合は true を戻し、それ以外は false を戻します。演算子の拡張は次のように定義されます。
operator-id = extension. procedure division using value a as string b as string returning r as condition-value.
文字列インスタンス間でオブジェクトが同一であるかどうかを確認するには、次の構文を使用します。
if a as object = b as object
従属文字列の拡張メソッドは、コンパイルの前にコンパイラであらかじめロードされます。これにより、従属文字列メソッドでは、.NET 環境の結果と JVM 環境の結果が同じになります。次に例を示します。
01 x string value “1234567890”. display x::Substring(2,3) “ The .NET way of doing things” display x::substring(2,3) “ The JDK way of doing things”
System.String::Substring(int, int) の .NET 動作は java.lang.String::Substring(int int) の JVM 動作と異なるため、拡張がインストールされない場合、.NET と JVM の結果は異なります。ただし、拡張がインストールされる場合、JVM 動作は .NET の動作と一致し、同じ結果になります。
従属文字列の拡張メソッドは、文字列タイプ (java.lang.String でもある) を拡張します。このメソッドは、.NET の従属文字列のメソッドの署名と一致する署名の文字列の新しいインスタンスを戻します。従属文字列のメソッドの拡張は次のように定義されます。
method-id Substring extension. procedure division using value a as string startIdx as binary-long sslength as binary-long returning r as string.