メソッド

メソッドは、その呼び出し時に実行されるコードのブロックを定義します。

method-header

メソッドのシグネチャメソッドの署名を使用すると、特定の要素のヘッダー内で、渡すパラメーターおよび戻り項目を指定できます。この署名を使用する場合は、メソッド内の手続き部のヘッダーを省きます。

method-id methodA static.
01 s string value "Hello".
procedure division.
   display s
end method.
method-id methodB (paramA AS binary-short paramB AS binary-short) RETURNING paramC AS binary-short.
set paramC = paramA * paramB
end method.

コアのサンプルも参照してください。このサンプルは、 Start > All Programs > Micro Focus Visual COBOL > Samples , under COBOL for JVM (Windows) or $COBDIR/demo (UNIX). に用意されています。

静的メソッドとインスタンス メソッド

メソッドは、静的メソッドまたはインスタンス メソッドのいずれかになり、デフォルトでインスタンスになります。インスタンス メソッドは、クラスの指定インスタンスで機能し、静的メソッドは指定インスタンスでは機能しません。

静的メソッドは、そのクラスの静的メソッドおよび静的フィールドにアクセスできます。また、そのクラスまたはその他のクラスのインスタンス メソッドおよびインスタンス フィールドにもアクセスできます。そのためには、必要なクラスのオブジェクトを作成し、そのオブジェクトを介してメソッドまたはフィールドにアクセスします。

たとえば、次のクラスには、静的メソッドとインスタンス メソッド、および静的フィールドとインスタンス フィールドが含まれます。

       class-id MyCounter.

       01 totalCount binary-long static. 
       01 myCount binary-long.
       
       method-id New.
       procedure division.
           set myCount to totalCount
           set totalCount to totalCount + 1
       end method.

       method-id GetCount.
       procedure division returning returnItem as binary-long.
           set returnItem to myCount
       end method.          

       method-id GetTotalCount static.
       procedure division returning returnItem as binary-long.
           set returnItem to totalCount
       end method.          

       method-id SetCount static.
       procedure division using by value aCount as binary-long.
           set totalCount to aCount 
       end method.
       end class.
       

次に、上記のコードを呼び出すプログラムを示します。

       program-id StaticInstanceProgram.
       01 myInstance1 type MyCounter.
       01 myInstance2 type MyCounter.
             
       procedure division.
           invoke type MyCounter::SetCount(10)
           set myInstance1 to new type MyCounter
           set myInstance2 to new type MyCounter
           display myInstance1::GetCount()         *> displays 10
           display myInstance2::GetCount()         *> displays 11
           display type MyCounter::GetTotalCount() *> displays 12
                      
       end program.
       

静的メソッドとインスタンス メソッドを使用するには、次のようにします。

  • 静的メソッドを宣言するには、STATIC キーワードを使用します。メソッドが静的クラスにある場合、STATIC を指定してください。
  • インスタンス メソッドを宣言するには、キーワード STATIC は使用しないでください。
  • 静的メソッドにアクセスするには、クラスの型指定子の後に :: を続けてメソッド名を使用します。たとえば、上記のコードのように「MyCounter::GetTotalCount()」と入力します。静的メソッドが現在のクラスにある場合、型指定子およびコロンは省略できます。
  • インスタンス メソッドにアクセスするには、インスタンス オブジェクトの名前の後に :: を続けてメソッドを使用します。たとえば、上記のコードのように「myInstance1::GetCount()」と入力します。

インスタンス メソッドの修飾子

次の修飾子は、メソッドが継承する範囲とメソッドが継承される範囲を示します。

ABSTRACT
抽象メソッドを宣言します。抽象メソッドは抽象クラスでのみ宣言でき、そのクラスから派生する非抽象クラスでオーバーライドする必要があります。
OVERRIDE

該当のメソッドが、同じ名前およびシグネチャの継承されたメソッドをオーバーライドすることを指定します。

REDEFINE

サブクラス型として定義される項目で再定義メソッドが呼び出された場合のみスーパークラスのメソッドを非表示にします。

FINAL
派生クラスがメソッドをこれ以上オーバーライドしないようにします。

抽象メソッド

抽象メソッドに実装はありません。抽象メソッドを使用するには、そのメソッドを継承し、継承するサブクラスで、メソッドの実装を提供します。抽象クラスには、抽象以外のメソッドと抽象メソッドを含めることができます。抽象以外のメソッドでは、デフォルトの実装が提供され、この実装はサブクラスのメソッドでオーバーライドできます。次に例を示します。

class-id MyAbstractClass abstract.     
       01 myField  value 0 binary-long property. 
       
       method-id AbstractMethod abstract. *> no implementation
       end method.
 
       method-id NonAbstractMethod.       *> not abstract
       procedure division.
            set myField to 1   *> Default behavior, which can be overridden
       end method.       

       end class.

抽象クラスの非抽象メソッドでは、デフォルトの実装が提供され、この実装はサブクラスのメソッドでオーバーライドできます。

       class-id MySubClass inherits type MyAbstractClass. 

       method-id AbstractMethod override.
       procedure division.
           *> Subclass method implements abstract method
           display "myField is: " myField  *> displays 0
       end method.
       
       method-id NonAbstractMethod override.
       procedure division.
           *> Subclass method overrides method that is not abstract
           set myField to 6  
           display "myField is: " myField  *> displays 6
       end method.
       
       end class.

抽象クラスでメソッドを定義して使用するには、次のようにします。

  • ABSTRACT キーワードを使用して抽象のメンバーを定義し、抽象以外のメンバーの場合は省略します。抽象メンバーは、抽象クラス内になければなりません。
  • 抽象スーパークラスを継承する非抽象クラスには、スーパークラスのすべての抽象メンバーの実装を含める必要があります。ただし、サブクラス自体が抽象であることがあり、この場合、実装を含める必要はありません。
  • スーパークラスのメソッドをオーバーライドするサブクラスのメソッドでは、キーワード OVERRIDE を使用する必要があります。
  • 抽象メソッドは private にはなりません。public、protected、internal、または protected internal になります。

オーバーライド メソッド

オーバーライド メソッドは、同じ名前およびシグネチャの継承される仮想メソッドをオーバーライドするメソッドです。

たとえば、サブクラス Circle がスーパークラス Shape を継承するとします。どちらのクラスにも Draw メソッドが含まれます。Circle サブクラスの Draw メソッドは、Shape スーパークラスの Draw メソッドをオーバーライドします。

       class-id Shape.
       method-id Draw.
           display "let's draw a super shape"
       end method.
       end class.
              
       class-id Circle inherits type Shape.
       method-id Draw override.
           display "let's draw a circle"
       end method.
       end class.

オーバーライド メソッドを定義するには、次のようにします。

  • method-id ヘッダーで OVERRIDE 修飾子を指定します。
  • メソッドの名前およびシグネチャが、継承されたクラスでオーバーライドされるメソッドと同じであることを確認します。

継承されたクラスでは最終メソッドをオーバーライドできません。仮想メソッド (最終として定義されていないメソッド) のみをオーバーライドできます。

再定義メソッド

REDEFINE キーワードは、クラスから継承されるメソッドを非表示にする場合に使用されます。継承されるメソッドを非表示にするには、そのメソッドを派生クラスで同じ名前を使用して宣言して、REDEFINE を指定します。

再定義メソッドは、スーパークラスのメソッドと同じ名前およびシグネチャのメソッドである点がオーバーライド メソッドと似ています。ただし、オーバーライド メソッドとは異なり、再定義メソッドは、その呼び出し方法に応じて実行する場合としない場合があります。メソッドが次のクラスにあるものとして定義される項目で呼び出されるとします。

  • スーパークラス。その項目が実際にサブクラスのインスタンスを保持する場合でも、スーパークラスのメソッドが実行されます。
  • サブクラス。サブクラスの再定義メソッドが実行されます。

たとえば、2 つのサブクラス Circle と Square があり、これらは Shapes スーパークラスから継承するとします。すべてのクラスに Draw メソッドがあります。Circle クラスの Draw メソッドは、Shapes スーパークラスの Draw メソッドをオーバーライドします。Square クラスの Draw メソッドは、Shapes スーパークラスの Draw メソッドを再定義します。

       class-id Shape.
       method-id Draw.
           display "let's draw a super shape"
       end method.
       end class.
              
       class-id Circle inherits type Shape.
       method-id Draw override.
           display "let's draw a circle"
       end method.
       end class.
       class-id Square inherits type Shape.
       method-id Draw redefine.
           display "let's draw a square"
       end method.
       end class.

Circle クラスおよび Square クラスは次のように呼び出すことができます。

       program-id. RedefineSampleProgram.
   
       01 myShape type Shape.  
       01 myCircle type Circle.  
       01 mySquare type Square.  
       
       procedure division.
           set myShape to new type Shape
           set myCircle to new type Circle
           set mySquare to new type Square
                      
           invoke myCircle::Draw   *> Circle Draw method is invoked
           invoke mySquare::Draw   *> Square Draw method is invoked
                                   *> Shape Draw method is hidden, redefined
 
           set myShape to myCircle *> Superclass instance is set to subclass 
           invoke myShape::Draw    *> Circle Draw method is invoked  
                                   *> as it overrides Shape Draw method

           set myShape to mySquare *> Superclass instance is set to subclass 
           invoke myShape::Draw    *> Shape Draw method is invoked as
                                   *> Square Draw method doesn't override it
       
       end program.

上記の呼び出しにより、次の出力が生成されます。

let’s draw a circle
let’s draw a square
let’s draw a circle
let’s draw a super shape

REDEFINE はまた、スーパークラスのメソッドが FINAL として定義されているためにオーバーライドできない場合にも使用されます。この場合、REDEFINE を使用してスーパークラスのメソッドを非表示にすることができます。

最終メソッド

メソッドを FINAL として宣言すると、そのメソッドをサブクラスでオーバーライドできなくなります。これにより、サブクラスでスーパークラスを継承できるようにしつつ、特定のメソッドをオーバーライドできないようにすることができます。

たとえば、MySuperClass から継承するクラス MySubClass があるとします。MySuperClass には、1 つの最終メソッドおよび 1 つの仮想 (最終ではない) メソッドがあります。サブクラスは仮想メソッドをオーバーライドしますが、最終メソッドの継承やオーバーライドはできません。

       class-id MySuperClass.     
              
       method-id VirtualMethod. *> Methods are virtual by default
       procedure division.
           display "Virtual method in the superclass"
       end method.
                    
       method-id FinalMethod final. *> Final methods are not virtual
       procedure division.
           display "Final method in the superclass"      
       end method.
              
       end class.

サブクラスは次のように定義されます。

       class-id MySubClass inherits type MySuperClass.     

       method-id VirtualMethod override.
       procedure division.
           display "Overridden virtual method in the subclass"
       end method.

      * method-id FinalMethod override. 
      *>COBCH0954 Method 'FinalMethod' cannot OVERRIDE a nonvirtual method
              
       end class.

これらのクラスは、次のように呼び出されます。

       program-id FinalOverrideSampleProgram.
       01 mySuper type MySuperClass.
       01 mySub type MySubClass.

       procedure division.
           set mySuper to new type MySuperClass
           invoke mySuper::FinalMethod()
           invoke mySuper::VirtualMethod()
           
           set mySub to new type MySubClass
           invoke mySub::VirtualMethod()

       end program.

上記のサンプル プログラムにより、出力が次のように生成されます。

Final method in the superclass
Virtual method in the superclass
Overridden virtual method in the subclass

最終メソッドはサブクラスでオーバーライドできませんが、サブクラスで再定義できます。

拡張メソッド

EXTENSION キーワードは、メソッドを拡張メソッドとして宣言します。

拡張メソッドを使用すると、コードの編集や再コンパイルを行わずにメソッドを既存の型に追加できます。拡張メソッドは、オブジェクト インスタンスで利用可能な追加メソッドとして表示されますが、別の場所に実装されます。

たとえば、次の拡張メソッドは、文字列の単語数を数えるメソッドを追加することで、文字列クラスを拡張します。
       class-id MyCount static.
       method-id CountWords extension. *> extension method is implicitly static
       procedure division using by value str as string 
                          returning wordCount as binary-long.
            set wordCount to str::Split(' ')::Length
       end method.
       end class.

拡張メソッドを宣言するには、次のようにします。

  • メソッド ヘッダーで EXTENSION キーワードを使用します。
  • メソッドは、ネストでも汎用でもない静的クラス内になければなりません。メソッドは暗黙的に静的になります。
  • メソッドの本文では、最初のパラメーターは拡張する型になります。これは、常に値パラメーターになります。

マネージ COBOL から拡張メソッドを呼び出すための特別な構文はありません。拡張メソッドは、他のメソッドと同様に使用されます。ただし、最初のパラメーターを、メソッドが呼び出されるインスタンスであるかのように指定する点を除きます。

parameter-1::method-name(more parameters)

JVM COBOL では、コンパイルの前にあらかじめロードされる拡張が 2 つあります。

  • 文字列同一の拡張演算子
  • 従属文字列の拡張メソッド

この 2 つの拡張により、JVM COBOL または .NET COBOL のどちらにコンパイルされるかにかかわらずマネージ COBOL で同じ結果が生成されるようになります。詳細は、「拡張メソッドと演算子」を参照してください。

同期メソッド

SYNC 修飾子は、メソッドに送られる引数の値をロックするため、メソッドの処理中に引数の値が変わることはありません。

引数がメソッドに送られ、参照パラメーターとして受け取られると、パラメーターの値およびその対応する送信引数が更新されることがあります。SYNC 修飾子を使用しない場合、メソッドの処理中に送信引数の値が不明になります。

メソッドに対する SYNC 修飾子は、SYNC 文のメソッド全体のラッピングに相当します。

次に例を示します。

       01 a binary-long value 20.
       ...
       invoke Adjust(a)
       display a                 *> displays 93
   
       method-id Adjust sync (reference x as binary-long).
           set x to x + 73
       end method. 
       end class.

上記の例では、SYNC 修飾子はメソッド Adjust() に適用されます。これにより、メソッド内の変数 (この場合、ローカルでは変数 x、呼び出しコード内では変数 a) には、メソッドの処理が終わるまでアクセスできません。

明示的なインターフェイスの実装

FOR 句を指定すると、このメソッドは明示的なインターフェイス メンバーの実装になります。このメソッドを明示的に呼び出すことはできません。このメソッドは、インターフェイス型にキャストされているこのクラスのインスタンスで対応するメソッドが呼び出された際に暗黙的に呼び出されます。

(FOR 句による) 明示的なインターフェイスの実装の使用は、クラスが 2 つの異なるインターフェイスを実装しており、これら 2 つのインターフェイスに同じ名前およびシグネチャを持つメソッドがある場合に特に役立ちます。この場合、FOR 指定を使用すると、2 つの異なるインターフェイス用にメソッドの 2 つの異なる実装を提供できます。
次に例を示します。
interface-id. "IAlarm1".
method-id checkAlarm.
procedure division using atime as binary-long.
end method.
end interface.

interface-id. "IAlarm2".
method-id checkAlarm.
procedure division using atime as binary-long.
end method.
end interface.

class-id BasicAlarm implements type IAlarm1 type IAlarm2.

method-id checkAlarm for type IAlarm1.
procedure division using atime as binary-long.
...
end method.

method-id checkAlarm for type IAlarm2.
procedure division using atime as binary-long.
...
end method.
end class.

非同期メソッド

注: .NET COBOL でのみサポートされます。

.NET COBOL では、.NET async および await 構文を使用して、応答性の高いアプリケーションを記述できます。

非同期プログラミングにより、アプリケーションで、メイン アプリケーション スレッドから独立して実行されるタスクを作成できます。つまり、タスクはビジー状態の場合には別のスレッドで実行されるため、アプリケーションの応答性が維持されます。完了後、タスクの結果には呼び出し元からアクセスできます。

非同期プログラミングは、完了までに長時間かかる操作を実行するアプリケーションで特に役立ちます。具体的には、インターフェイスの応答性を保つ必要がある、Web 要求を処理するアプリケーションや、ビッグ データまたは複数のリソースを処理するデスクトップ アプリケーションなどが挙げられます。

非同期アプリケーションは、メソッドおよび待機する対象の非同期操作を示す ASYNC および AWAIT 構文を使用して作成できます。アプリケーションの実行をメイン スレッドで継続しながら非同期操作を別のスレッドで実行するための複雑な作業は、すべてコンパイラーによって処理されるため、プログラマーにとっては非常に単純です。また、コンパイラにより、待機中のメソッドに制御が返された際に正しい操作を実行できます。

次の例では、ASYNC キーワードはメソッドが非同期メソッドであることを示しています。実行が AWAIT の呼び出しに到達すると、1000 ms の遅延を実行する .NET Task メソッドに制御が渡されます。遅延後、制御は呼び出し元メソッドに戻されます。

method-id ProcessItemsAsync async (#type as string,
                                   #count as binary-long)
                            yielding items as string occurs any.
     invoke await type Task::Delay(1000)
     set items to table of ("a", "b", "c")
 end method.

非同期操作を開始し、その完了後にメソッドの実行を継続するには、AWAIT キーワードを使用します。下記のコードでは、非同期操作は、Task スレッド クラスの Delay メソッドによって実行される遅延です。

invoke await type Task::Delay(1000)

Task は、非同期コードを記述するための型を含む System.Threading.Tasks ネームスペースに属しています。Task には、コードを非同期的に実行するうえで役立つメソッドが複数含まれています。

下記の例では、完了後に ProcessItemsAsync の結果が items に移動されることになります。

declare items = await ProcessItemsAsync("user", 8)

await 文を使用するイベント ハンドラーを含む async-void を使用します。次の例では、意図的な遅延により、ボタンは無効になっています。非同期の操作であるため、コードの残りの部分は、UI スレッドをブロックすることなく、1000 ms の遅延後に実行が継続されます。

method-id button1_Click async-void (sender as object,
                                    e as type EventArgs).
     set button1::Enabled to false
     invoke await type Task::Delay(1000)
     set button1::Enabled to true
 end method.

次の例は、結果を返さない async メソッドを示しています。

method-id Task ProcessAsync() async.
     invoke await type Task::Yield()
     invoke type Console::WriteLine("async...")
 end method.

次の async メソッドは結果を返します。

method-id ProcessItemsAsync async (#type as string,
                                   #count as binary-long)
                            yielding items as string occurs any.
     invoke await type Task::Delay(1000)
     set items to table of ("a", "b", "c")
 end method.

メソッドでタスクの値を返す必要がある場合は、async-value キーワードを使用します。

注: async-value を使用するには、System.Threading.Extensions.dll への参照をプロジェクトに含める必要があります。NuGet (.NET パッケージ マネージャー ユーティリティ) パッケージの参照を使用することをお勧めします。必要な NuGet パッケージは、https://www.nuget.org/packages/System.Threading.Tasks.Extensions/です。
method-id AProcess async-value (x as condition-value)
                   yielding result as string.
     if x
         invoke await type Task::Delay(1000)
         set result to "x"
     else
         set result to "y"
     end-if
 end method.