Overloading is a programming technique where you overload a method name with more than one implementation.
For example, there may be a method called CalculateBill, taking as a parameter a list of purchases. We may want to have a variant of this method which, in addition to the list of purchases, has another parameter representing a discount code. The two methods can have the same name, and, when the method is invoked, the required one is selected based on the presence or absence of the discount code parameter.
In managed COBOL, it is permissible (and very common practice) for a given class or interface to have multiple methods with the same name, which are distinguished from each other by having different signatures. The signature of a method is determined by the:
When code written in COBOL, or any other language, attempts to invoke a method in some class or interface, and more than one method with the given name exists, then a choice of the available methods is made. The choice is based on the number of parameters and the ‘best’ type match for the parameters (the precise definition of ‘best’ is complex and is treated later). If no method with a suitable signature can be found, a ‘method not found’ error results. If there is no method that provides a better match than all the other possible methods with this name, an ‘ambiguous match’ error is given.
Although the type of the RETURNING item is a part of the method's signature, the choice of method to be invoked is never based on the RETURNING type. This is different from .NET and JVM, which allow a class to have multiple methods that differ only by returning type. This is not generally good practice, and COBOL produces an error if it finds such multiple methods within a single class. The only time when such multiple methods can arise from a COBOL program is in the case of the explicit and implicit operators.
The type of a COBOL parameter may be specified by:
For REFERENCE and OUTPUT parameters, managed types are exposed directly as managed pointers to the corresponding managed type. COBOL data types that do not correspond to managed types (such as PIC X fields, groups, numeric fields other than BINARY-LONG and so on) are exposed as COBOL pointers (objects of type MicroFocus.COBOL.Program.Reference, in .NET COBOL).
For VALUE parameters, and RETURNING items, types are exposed as follows:
Method overload resolution takes place in two phases:
Formally, the method overload that is ‘better’ than any other of the applicable method overloads is chosen. If no single overload is determined to be better than any other overload, then the match is considered to be ambiguous, and a compiler error results.
If this process does not result in a unique best match, an error (ambiguous match error) is produced
We start with the complete set of all methods with the required name that exist in the target class or in any of its parent classes (excluding those with identical signatures). From this set, any methods that are not visible from the invoking program, based on the visibility attribute encoded into the method, are discarded as follows:
Attribute | Visibility |
---|---|
PRIVATE | Visible only if the calling class is the same as the target class |
PUBLIC | Always visible |
PROTECTED | Visible only if the calling class derives from the target class |
INTERNAL | Visible only if the target class is in the same assembly as the calling class |
PROTECTED INTERNAL | Visible only if the calling class derives from the target class, or the calling class is in the same assembly as the target |
Next, if the required method is an instance method (a method that is applied to an object instance), all static methods are discarded. Similarly, if the required method is a static method, all instance methods are discarded.
Now each method is examined in turn to see whether it is applicable in normal form. This is determined as follows:
For a REFERENCE or OUTPUT parameter, the type of the argument must be exactly the same as the parameter type.
For a VALUE parameter, an implicit conversion must exist from the type of the argument to the type of the corresponding parameter. If any of these implicit conversions require truncation, this method is said to be a ‘truncation match’.
If a method is not applicable according to these rules, an attempt is made to match the method in expanded form, if both the following are true:
The expanded form of the method is derived from the normal form by replacing the array parameter by zero or more value parameters of the same type as the array element, so that the total number of parameters is the same as the number of invoking arguments. If the class already contains a method in normal form with this same expanded signature, then this method is determined to be not applicable. Otherwise the expanded form is tested for applicability in the same way as the test for normal form above.
After the above selection procedure:
ambiguous match
method not found
Suppose we have an argument list A with a set of argument types (A1, A2,…An) and two applicable method overloads Mp and Mq with parameter types (P1…Pn) and (Q1,…Qn), where if necessary the parameters have been converted to expanded form (see above).
Then, Mp is said to be a better method overload than Mq if both the following are true:
Suppose we have two conversions, from type S to types T1 and T2, then T1 is the better conversion when T1 and T2 are as follows:
T1 is: | And T2 is one of: |
same as S | anything |
binary-char |
|
binary-short |
|
binary-long |
|
binary-double |
|
Otherwise neither conversion is better.
The following example shows how, even in the case where the supplied argument is not exactly the same as that for any of the target methods, the “most specific” rule takes effect.
class-id. Class1. method-id main static. 01 objAnyObject object. 01 objString string. 01 objDateTime type "DateTime". 01 ref1 type "Class1". procedure division. set ref1 to new Class1 invoke ref1::"method1" *> uses variant 1 invoke ref1::"method1" (objAnyObject) *> uses variant 2 invoke ref1::"method1" (objString) *> uses variant 3 invoke ref1::"method1" (objDateTime) *> uses variant 2, *>because an object of type System.DateTime can be *>assigned to a System.Object but not to a System.String end method main. method-id method1. *> Variant 1 (no parameters) procedure division. display "variant 1" end method method1. method-id method1. *> Variant 2 (object parameter) procedure division using by value objAnyObject as object. display "variant 2" end method method1. method-id method1. *> Variant 3 (string parameter) procedure division using by value objString as string. display "variant 3" end method method1. end class Class1.
An implicit conversion exists from type S to type T if it is possible to assign an object of type S to an object of type T, with the equivalent of the following statement:
SET obj-T TO obj-S
The following types of implicit conversion are available:
This simply means that S is the same type as T
COBOL generally allows any numeric item to be assigned to any other, with any loss of data being the responsibility of the user. However, when determining if a conversion exists for the purposes of method overloading, we distinguish between proper conversions and truncation conversions. Proper conversions are those for which no loss of data magnitude should result, though even in this case precision can be lost when converting between fixed point and floating point.
For managed COBOL types, proper implicit conversions include:
binary-char |
|
binary-char unsigned |
The same as binary-char, plus:
|
binary-short |
|
binary-short unsigned |
The same as binary-short, plus:
|
binary-long |
|
binary-long unsigned |
The same as binary-long, plus:
|
binary-double |
|
binary-double unsigned |
The same as binary-double |
character |
|
float-short |
|
The value 0 may be converted to any enum type
If S and T are reference types, S may be assigned to T when:
Any value type can be converted to a System.Object, type by a process known as boxing. As part of this process the value is copied onto the object heap and a reference is created
Any numeric constant can be converted to any type capable of containing the full value of that constant. For instance the value 1 can be converted both to BINARY-CHAR UNSIGNED and to BINARY-CHAR.
A user defined implicit conversion consists of an optional implicit numeric conversion from S to S1 followed by the execution of a user defined implicit conversion operator resulting in T1, followed by another optional implicit numeric conversion to T. User defined implicit conversion operators are defined in COBOL with OPERATOR-ID IMPLICIT and result in methods with the name op_Implicit.