Methods

A method defines a block of code to be executed when the method is invoked.

method-header

Method SignatureThe method signature enables you to specify passing parameters and returning items within the header of certain elements. You must omit the procedure division header within the method if you use this signature.

Examples

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.

See also the Core sample, which is available from $COBDIR/demo.

Static and Instance Methods

Methods can be either static methods or instance methods, and are by default instance. An instance method operates on a given instance of a class, whereas a static method does not operate on a given instance.

A static method can access static methods and static fields in its own class. It can also access instance methods and fields, in its own class or any other class, by constructing an object of the required class and accessing the method or field via that object.

For example, the following class contains static and instance methods, and static and instance fields.

       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.
       

Here is the program that invokes the above code:

       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.
       

To use static and instance methods:

  • To declare a static method, use the STATIC keyword. If the method is in a static class, you must specify STATIC.
  • To declare an instance method, don't use the keyword STATIC.
  • To access a static method, you use the type-specifier for the class, followed by :: and then the method name. For example: type MyCounter::GetTotalCount() as in the above code. If the static method is in the current class, you can omit the type-specifier and the colons.
  • To access an instance method, you use the name of the instance object, then :: followed by the method. For example: myInstance1::GetCount() as in the above code.

Instance Method Modifiers

The following modifiers indicate the extent to which the method inherits and is inherited:

ABSTRACT
declares an abstract method. Abstract methods can only be declared in abstract classes, and must be overridden in any non-abstract class that derives from it.
OVERRIDE

specifies that this method overrides the inherited method of the same name and signature.

REDEFINE

hides the superclass method only when the redefine method is invoked on an item defined as being of the subclass type.

FINAL
prevents a derived class from further overriding the method.

Abstract Methods

An abstract method has no implementation. To use an abstract method, you inherit it and in the inheriting subclass, you provide the implementation of the method. An abstract class can contain methods that are not abstract, as well as abstract ones. The methods that aren't abstract can provide a default implementation, which can be overridden by a subclass method. For example:

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.

Non-abstract methods in an abstract class can provide a default implementation, which can be overridden by a subclass method.

       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.

To define and use methods in abstract classes:

  • You use the ABSTRACT keyword to define any members that are abstract, and omit it for members that are not abstract. Abstract members must be within an abstract class.
  • A non-abstract class inheriting an abstract superclass must contain an implementation of all the abstract members of the superclass. However the subclass itself can be abstract an in this case need not contain implementation.
  • Subclass methods which override methods in the superclass must use the keyword OVERRIDE.
  • An abstract method cannot be private. It can be public, protected or internal, or protected internal.

Override Methods

An override method is a method that overrides an inherited virtual method of the same name and signature.

For example, a subclass Circle inherits from a superclass Shape. Both classes contain a Draw method. The Draw method in the Circle subclass overrides the Draw method in the Shape superclass.

       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.

To define an override method:

  • Specify the OVERRIDE modifier in the method-id header
  • Ensure that the method has the same name and signature as the method to be overridden in the inherited class

You cannot override a final method in the inherited class. You can override only virtual methods (methods not defined as final).

Redefine Methods

The REDEFINE keyword is used to hide a method inherited from a class. To hide an inherited method, declare it in the derived class using the same name and specify REDEFINE.

A redefine method is similar to an override method, in that it is a method with the same name and signature as a method in the superclass. However, unlike an override method, the redefine method might or might not be executed depending on how it is invoked. When the method is invoked on an item defined as being in the:

  • Superclass, even when that item actually holds an instance of the subclass, the superclass method is executed.
  • Subclass - the subclass redefine method is executed.

For example, we have two subclasses Circle and Square, which inherit from the Shapes superclass. All the classes have a Draw method. The Circle class's Draw method overrides the Draw method in the Shapes superclass. The Square class's Draw method redefines the Draw method in the Shapes superclass.

       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.

You can invoke the Circle and Square classes as follows:

       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.

The above invocations produce the following output:

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

Another place to use REDEFINE is where the method in the superclass is defined as FINAL and so it cannot be overridden. In this case, you can use REDEFINE to hide the superclass method.

Final Methods

A method that is declared as final is prevented from being overridden in any subclasses. This enables a class to inherit a superclass, while preventing a specific superclass method from being overridden in the subclass.

For example, we have a class MySubClass, which inherits from MySuperClass. MySuperClass has one final method and one virtual (not final) method. The subclass overrides the virtual method, but it cannot inherit or override the final method.

       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.

The subclass is defined as follows:

       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.

The classes are invoked as follows:

       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.

The above sample program produces output as follows:

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

Although a final method cannot be overridden in a subclass, it can be redefined in the subclass.

Extension Methods

The EXTENSION keyword declares a method as an extension method.

Extension methods enable you to add methods to existing types without the need to edit or recompile the code. Extension methods appear as additional methods available on an object instance while they are implemented elsewhere.

For example, the following extension method extends the string class, by adding a method to count the number of words in a string:
       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.

To declare an extension method:

  • Use the EXTENSION keyword in the method header.
  • The method must be in a static class that is not nested or generic. The method is implicitly static.
  • In the method body, the first parameter is the type to extend. It is always a value parameter.

There is no special syntax for calling extension methods from JVM COBOL. You consume an extension method in the same way as any other method, except you specify the first parameter as if it were the instance on which the method is called:

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

In JVM COBOL, there are two extensions that are preloaded before compilation:

  • An extension operator for string equality
  • An extension method for substring

These two extensions ensure that you receive the same results, regardless of whether the code is compiled to JVM COBOL or .NET COBOL. For more detail, see Extension Methods and Operators.

Synchronized Methods

The SYNC modifier locks the values of the arguments sent to the method, so that they do not change while the method is processing.

When an argument is sent to a method and is received as a reference parameter, the value of the parameter and its corresponding sending argument might be updated. Without the SYNC modifier, the value of the sending argument is unknown, while the method is processing.

The SYNC modifier on a method is equivalent to wrapping the whole method in a SYNC statement.

For example:

       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.

In the above example, the SYNC modifier is applied to the method Adjust(). This ensures that the variables within the method (in this case, the variable x locally, but variable a in the invoking code) cannot be accessed until the method has finished processing.

Explicit Interface Implementation

If the FOR clause is specified, this method is an Explicit Interface Member Implementation. The method may not be invoked explicitly. It will be invoked implicitly when the corresponding method is invoked on an instance of this class which has been cast to the interface type.

Use of explicit interface implementation (via the FOR clause) is particularly useful when the class implements two different interfaces, and these two interfaces have a method with the same name and signature. In this case, by using the FOR phrase, you can supply two different implementations of the method for the two different interfaces.
For example:
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.

Asynchronous Methods

Note: Only supported in .NET COBOL.

.NET COBOL utilizes the .NET async and await syntax to enable you to write responsive applications.

Asynchronous programming enables you to create tasks in your application that run independently from the main application thread. This means that when a task is busy, it executes in a different thread and your application remains responsive. After completion, the result of the task can be accessed by the caller.

Asynchronous programming is especially useful in applications that perform operations taking a long time to complete. These could be applications that handle web requests where you need the interface to stay responsive, or desktop applications that process big data or multiple resources.

You can create asynchronous applications by using the ASYNC and AWAIT syntax which indicate the method and the asynchronous operation it is waiting for. With it being simple enough for the programmer, the Compiler handles all the complicated work of running the asynchronous operation in a different thread, while the application continues to run in the main thread. The Compiler also ensures the correct operations are executed when the control is returned to the method at the await place.

In the following example, the ASYNC keyword indicates that your method is an asynchronous one. When execution reaches the AWAIT call, control is given to the .NET Task method that performs a delay of 1000ms. After the delay, control is given back to the method:

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.

Use the AWAIT keyword to begin an asynchronous operation and then continue executing the method after it completes. In the code below, the asynchronous operation is a delay performed by the Delay method of the Task threading class:

invoke await type Task::Delay(1000)

Task belongs to the System.Threading.Tasks namespace which includes types for writing asynchronous code. Task includes a number of methods that are useful for running code asynchronously.

The example below shows the result of ProcessItemsAsync being moved to items after completion:

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

Use async-void with an event handler that uses await statements. In the following example, the button is disabled with an artificial delay. The operation is asynchronous so the rest of your code will continue to execute after the delay of 1000ms without blocking the UI thread:

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.

The following example shows an async method which does not return a result:

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

The following async method returns a result:

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.

Use the async-value keyword if your method should return a value task.

Note: In order to use async-value, your project must include a reference to System.Threading.Extensions.dll - preferably using a NuGet (the .NET package manager utility) package reference. The required NuGet package is: 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.