Similar to other managed code languages such as C#, managed COBOL supports extension methods, which enable you to add methods to existing types thereby providing additional functionality without the need to edit or recompile the code.
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 str as string returning wordCount as binary-long. set wordCount to str::Split(' ')::Length end method. end class.
You extend a method when you cannot override it. The method you want to extend might be in a final class or in code that you don't have access to. In these cases, you can define and compile the extension method separately and then use it freely.
Use the following construction to define an extension method:
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.
To define an extension method:
There is no special syntax for calling extension methods. You consume extension methods in much the same way as any other method, except you specify the first parameter as the type that is extended. You use this syntax:
parameter-1::method-name(more parameters)
In the example below, you consume with code such as 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.
You use extension methods as if they were instance methods defined in another class or interface. Extension methods appear as additional methods available on an object instance and yet they are implemented elsewhere. Intellisense shows all methods you can use including the extension methods that are declared elsewhere. An extension method is indicated with a different icon and its tooltip includes the phrase (extension).
When you invoke an extended method, the extension method is not contained in the class specified in the invoking statement. It is contained in another class elsewhere. The invoking and executing classes are different. The method's defined and execution containing types are different.
Extension methods work like this:
Extension operators are very similar to extension methods. They enable you to provide alternative behavior for an operator, without the need to edit or recompile the code. You define and use extension operators in the same way as extension methods.
For example, you could define and use an extension plus operator, as follows:
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.
You extend an operator when you cannot overload it. The operator you want to extend might be in a final class or in code that you don't have access to. In these cases, you can define and compile the extension operator separately and then use it freely.
To declare and use an extension operator:
Extension operators are implemented as methods, such as an op_Equality or op_Addition method. This method is added to a specific dictionary in TypeInfo. This enables the code looking for operator overloads and extensions to look in this dictionary in addition to the classes of the two operands.
When you invoke a method, you specify the class in which the method belongs. In our example, the method CheckWords() in the myWord class and is invoked as follows:
01 myWord type MyWords. 01 myDoc string. 01 sizeOK condition-value. set sizeOK to myWord::CheckWords(myDoc) ... class-id MyWords final. method-id CheckWords. ...
In the above, since the class can be found, so can the method. The class loader architecture uses the class on which a method is defined to know when to load a class. In this case, the myWord class is loaded before the method CheckWords() is invoked. This happens in much the same way during compilation. When the myWord::CheckWords method is referred to in a class that is being compiled, the Compiler uses a class loader to load the CheckWords method to resolve the method signature and understand how to generate the appropriate code.
However, when you invoke an extended method (or operator), the extension method is not contained in the class specified in the invoking statement. In our example, the extension method CountWords() is invoked on the type of the aString variable (which is a string), but it is executed in your extension class, as follows:
01 aString string. 01 myWordCount binary-long. set myWordCount to aString::CountWords ... class-id MyCount static. method-id CountWords extension. ...
Although the method is invoked on the String class, the method to be executed is contained in a different class (the MyCount class). The class loader can't find the method, because its defined and execution containing types are different. For the Compiler to use extension methods (and extension operators), it has to load their defining classes before compiling the classes that refer to those extensions.
To ensure that the Compiler can find the extensions, you use ILREF directive at the command line, combined with the JVMGEN directive. The following example loads the class defined within the ops.class file. The name and package of the class defined in ops.class does not need to be the same as the name and directory of the class file.
Windows: |
cobol x.cbl jvmgen ilref(ops.class); |
UNIX: |
cob x.cbl –C jvmgen -C ilref(ops.class) |
The following command unpacks the file ops.jar and loads the moreOps.class. Note that the .jar file needs to be on the CLASSPATH environment variable or specified in the JVMCLASSPATH directive for it to work.
Windows: |
cobol -j x.cbl jvmclasspath(ops.jar) ilref(moreOps.class); |
UNIX: |
cob -j x.cbl -C jvmclasspath(ops.jar) –C ilref(moreOps.class) |
The ILREF directive requires a file in class format. Files with the .class extension are treated as class format.
The following extensions are supplied for JVM:
The classes containing the extensions are preloaded by the Compiler by using the ILREF directive.
An extension operator for string equality is preloaded by the Compiler before compilation. This ensures that a check for equality gives the same results in the .NET environment as in the JVM environment. For example, the following code compares two strings:
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
Without the preloaded extension, the code sees the strings as different, because the JVM java.lang.String::equality(string) method compares the references and the .NET System.String::equality(string) method compares the values. With the preloaded extension, the code sees the strings as the same.
The string equality extension extends both the = and equals operators for the type string. The operator returns true if the two string instances contain exactly the same character sequence, false otherwise. The operator extension is defined as:
operator-id = extension. procedure division using value a as string b as string returning r as condition-value.
To check for object equality between string instances use:
if a as object = b as object
An extension method for Substring is preloaded by the Compiler before compilation. This ensures that the Substring method gives the same results in the .NET environment as in the JVM environment. For example:
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”
Since, the .NET behavior of System.String::Substring(int, int) differs from the JVM behavior of java.lang.String::Substring(int int), you get different results for .NET and JVM, if the extension is not installed. However, if the extension is installed, the JVM behavior matches that of .NET and you get the same results.
The Substring extension method extends the string type (which is also java.lang.String). This method returns a new instance of string with a signature that matches that of the Substring method in .NET. The Substring method extension is defined as follows:
method-id Substring extension. procedure division using value a as string startIdx as binary-long sslength as binary-long returning r as string.
An extension operator for string equality is preloaded by the Compiler before compilation. This ensures that a check for equality gives the same results in the .NET environment as in the JVM environment. For example, the following code compares two strings:
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
Without the preloaded extension, the code sees the strings as different, because the JVM java.lang.String::equality(string) method compares the references and the .NET System.String::equality(string) method compares the values. With the preloaded extension, the code sees the strings as the same.
The string equality extension extends both the = and equals operators for the type string. The operator returns true if the two string instances contain exactly the same character sequence, false otherwise. The operator extension is defined as:
operator-id = extension. procedure division using value a as string b as string returning r as condition-value.
To check for object equality between string instances use:
if a as object = b as object
An extension method for Substring is preloaded by the Compiler before compilation. This ensures that the Substring method gives the same results in the .NET environment as in the JVM environment. For example:
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”
Since, the .NET behavior of System.String::Substring(int, int) differs from the JVM behavior of java.lang.String::Substring(int int), you get different results for .NET and JVM, if the extension is not installed. However, if the extension is installed, the JVM behavior matches that of .NET and you get the same results.
The Substring extension method extends the string type (which is also java.lang.String). This method returns a new instance of string with a signature that matches that of the Substring method in .NET. The Substring method extension is defined as follows:
method-id Substring extension. procedure division using value a as string startIdx as binary-long sslength as binary-long returning r as string.