Types and Type Conversion

Value types, reference types and boxing

Managed COBOL distinguishes between:

  • Value types (such as binary-long and System.DateTime in .NET). Value type data items contain the actual data. For example, a COBOL binary-long data item contains a 32 bit integer.
  • Reference types, which are allocated on the object heap. Reference type data items hold a reference into the object heap. Reference types are under the management of the garbage collector, which will reclaim the space on the object heap at the point when there are no longer any active references.

All value types can be turned into reference types by a process known as boxing. For example, you can set an object reference to a value type, such as a binary-long. During boxing, the system copies the value into the object heap and returns a reference to it.

Boxing happens automatically when needed, for example when a value type is passed as a parameter to a method that expects an object as a parameter.

You can box explicitly by assigning a value type to a generic object (such as System.Object in .NET COBOL or java.lang.Object in JVM COBOL). You can unbox value types to restore the original value type.

When you specify TYPE classname in the data item definition:

  • If classname represents a reference type, you get a reference type object.
  • If classname represents a value type, you get a value type object.

See the ValueTypes sample, available from Start > All Programs > Micro Focus Enterprise Developer > Samples > Visual COBOL Samples and under COBOL for .NET.

Type information

Every class has an associated root object, which can be used to get information about the class such as its fields and methods. To obtain type information, you use the following:

  • In NET COBOL, GetType() - to obtain the System.Type object for an object reference
  • In JVM COBOL, getClass() - to obtain the java.lang.Class object reference
  • TYPE OF - to obtain the System.Type or java.lang.Class object for a class, interface, delegate or enumeration
    imperative-clause TYPE OF type-name
  • TYPE OF type-name[ANY...] - to obtain the System.Type (.NET) or java.lang.Class (JVM) object for a generic class, interface, or delegate.

For example, in .NET COBOL:

*>Value Types
*>condition-value
*>binary-char (unsigned)
*>character
*>binary-short, binary-long, binary-double (unsigned)
*>float-short, float-long
*>decimal
*>DateTime (a framework type)

*>Reference types
*>object
*>string

*>Initializing
declare correct as condition-value = true
*> Can also infer variable type if the value has a well-defined type
declare incorrect = false  *> automatically a condition-value
declare b as byte = h"2a"  *> hex
declare o as byte = o"52"  *> octal
declare b2 as byte = b"101010" *> binary
declare person as object = null
declare nam as string = "Dwight"
declare grade as character = "B"
declare now as type DateTime = type DateTime::Now
*> No support for date/time literals
declare amount as decimal = 35.99
declare gpa as float-short = 2.9
declare pi as float-long = 3.14159265
declare lTotal as binary-double = 123456
declare sTotal as binary-short = 123
declare usTotal as binary-short unsigned = 123
declare uiTotal as binary-long = 123
declare ulTotal as binary-long unsigned = 123

*>Type Information
declare x as binary-long
display x::GetType            *> Prints System.Int32
display type of binary-long   *> Prints System.Int32
display x::GetType::Name      *> Prints Int32
*>Type Conversion
declare f as float-short = 3.5   *> automatic conversion
declare i = f as binary-long     *> set to 3 (truncates decimal)

end program.

program-id Legacy.
*> COBOL types not supported directly by other languages.
*> Visual COBOL supports these types on all platforms.
*> Only a few examples here
01 displayNumber pic 9(9).99.
01 computeNumber pic 9(9)V99.
01 alphaNumberic pic a(23).
01 binaryStorage pic x(12).
*> Also groups and redefines - a few examples
01 arecord.
   03 aSubRecord pic x(10).
   03 aUnion     pic 9(10) redefines aSubrecord.
end program.

Type conversions

A type conversion allows an expression of some type to be converted to a different type. There are two kinds of type conversion, explicit and implicit. In order to perform an explicit conversion, it is necessary to use the AS or AS IF phrase, specifying the required target type. An example of this type of conversion is:

Declare myAnimal as type Animal
Declare myWarthog as type Warthog
…
Set myWarthog to myAnimal as type Warthog    *> Convert type Animal to type Warthog

The explicit conversion is necessary because not all objects of type Animal are of type Warthog.

An example of an implicit conversion is:

Set myAnimal to myWarthog

Because type Warthog derives from type Animal, the conversion is always legitimate and can be done implicitly. Similarly, using implicit conversion, myWarthog can be passed as an argument to any method that has a by value parameter of type Animal.

Implicit conversions

COBOL allows the following kinds of implicit conversion:

  • Implicit numeric conversions
  • Implicit enum conversions
  • Implicit reference conversions
  • Boxing conversions
  • User-defined implicit conversions
  • Anonymous method conversions
  • Method group conversions

The main contexts in which implicit conversions occur are:

  • Assignments (e.g. SET and DECLARE statements)
  • Parameter passing

Implicit enum conversions

The numeric literal 0 can be converted to any enum type.

Note: This kind of version is allowed in .NET COBOL, but not in JVM COBOL.

Implicit reference conversions

An implicit conversion from type S to type T is allowed in the following cases:

  • S is derived from T
  • T is an interface type and S implements T
  • S and T are both arrays of the same dimensionality and:
    • The array elements of S and T are both reference types and;
    • An implicit reference conversion exists from the element type of S to the element type of T

Note that in .NET COBOL:

  • All array types derive from the type System.Array
  • All single dimensional arrays, e.g. type S[] implement the interface System.Collections.Generic.IList[S]
  • All delegate types derive from the type System.MulticastDelegate, which itself defives from System.Delegate

Some examples of implicit reference conversions:

01 appDomain type System.AppDomain.
01 obj object.
01 myList type List[string].
01 myEnumerable type IEnumerable[string].
01 objArray object occurs 10.
01 strArray string occurs 10.

set obj to appDomain       *>   type System.AppDomain derives from System.Object
set appDomain to obj       *> produces compile time error
set myEnumerable to myList *> List[string] implements IEnumerable[string] 
set objArray to strArray   *> source element type derives from target element type

Boxing conversions

In .NET COBOL, any value type can be converted to a reference type, either of type System.Object or of type System.ValueType. An enum type can also be converted to type System.Enum.

In JVM COBOL, the primitive types may be converted to boxed types as follows:
  • Binary-char can be converted to java.lang.Byte
  • Binary-short can be converted to java.lang.Short
  • Binary-long can be converted to java.lang.Integer
  • Binary-double can be converted to java.lang.Long
  • Condition-value can be converted to java.lang.Boolean
  • Character can be converted to java.lang.Character
  • Float-short can be converted to java.lang.Float
  • Float-long can be converted to java.lang.Double

User-defined implicit conversions

A user defined implicit conversion exists from type S to type T if there exists an implicit user defined type conversion operator (see below) from type S1 to type T1 such that:

  • S is the same as S1 or there exists a non-user-defined conversion from S to S1
  • T is the same as T1 or there exists a non-user-defined conversion from T1 to T

Anonymous method conversion

An anonymous method with signature S can be converted to any delegate type with the same signature and return type. Also, an anonymous method with no parameters can be converted to any delegate type with the same return type.

Method group conversions

A method group with method name method-name can be converted to a delegate type with signature S and return type R if there exists a method within that method group (i.e. a method with the specified name) with the same signature and return type.

Explicit conversions

Explicit conversions are specified using the AS [ IF ] phrase. They can be used anywhere that an expression of the target type is permitted.

COBOL allows the following kinds of explicit conversion:

  • All implicit conversions including numeric conversions
  • Explicit enum conversions
  • Explicit reference conversions
  • Unboxing conversions
  • User-defined explicit conversions

Explicit enum conversions

Any enum type may be explicitly converted to any numeric type.

Any numeric type may be explicitly converted to any enum type.

Explicit reference conversions

An explicit conversion from type S to type T (also known as a cast) is allowed in the following cases:

  • Type T is derived from type S
  • Type S is a class type and type T is an interface type and S does not implement T and S is not final
  • Type S is in interface type and type T is a class type, and T is not final or T implements S
  • S and T are both interface types, and S is not derived from T
  • S and T are both array types with the same dimensionality and:
    • The array elements of S and T are both reference types and;
    • An explicit reference conversion exists from the element type of S to the element type of T

If the cast fails, an exception is thrown (InvalidCastException for .NET or ClassCastException for JVM). To avoid this, you can: test for a valid cast first using INSTANCE OF; wrap the cast in an exception block (see the TRY Statement for more information); or use the AS IF phrase, which in the case of an invalid cast sets the target object to null and does not throw an exception (see cast expressions for more information).

Some examples of explicit reference conversions:
01 appDomain type System.AppDomain.
01 obj object.
01 myList type List[string].
01 myEnumerable type IEnumerable[string].
01 objArray object occurs 10.
01 strArray string occurs 10.

*> Following allowed as type System.AppDomain derives from object
set appDomain to obj as type System.AppDomain
*> Following allowed as list[string] implements IEnumerable[string]
set myList to myEnumerable as type List[string]
*> Following allowed as there exists an explicit reference conversion
*> from object (the element type of objArray) to string (element type of strArray)
set strArray to objArray as string occurs any
*> explicit conversions can be used anywhere…
display obj as type System.AppDomain
set obj to type of object    *> sets obj to a System.Type object
*> The following will throw an exception
set appDomain to obj as type System.AppDomain
*>
*> this statement does not fail, but sets myString to null
*> set myString to myObject as if string
An exception is thrown in the following as we are claiming obj-object references a System.AppDomain instance where in fact it's referencing a System.Type instance.
*> these statements fail if uncommented  
*> set obj-object to obj-type                 
*> set obj-app-domain to obj-object as type System.AppDomain

Unboxing conversions

In .NET COBOL, items of type System.Object or of type System.ValueType can be explicit converted to a value type. Items of type System.Enum can be converted to enum types.

For example:

01 obj object.
01 dt type System.DateTime.

Set obj to dt                          *> this is an implicit conversion using boxing
Set dt to obj as type System.DateTime  *> explicit unboxing conversion
In JVM COBOL, following types may be converted to primitive types:
  • java.lang.Byte can be converted to binary-char
  • java.lang.Short can be converted to binary-short
  • java.lang.Integer can be converted to binary-long
  • java.lang.Long can be converted to binary-double
  • java.lang.Boolean can be converted to condition-value
  • java.lang.Character can be converted to character
  • java.lang.Float can be converted to float-short
  • java.lang.Double can be converted to float-long

User defined explicit conversions

A user defined explicit conversion exists from type S to type T if there exists an explicit user defined type conversion operator (see below) from type S1 to type T1 such that:

  • S is the same as S1 or there exists a non-user-defined explicit conversion from S to S1
  • T is the same as T1 or there exists a non-user-defined explicit conversion from T1 to T

Creating user-defined conversion operators

A conversion operator converts a data item from one data type into another, such as from a type that you have defined into an integer. Conversion operators can be overloaded, so that the appropriate conversion operator is used according to the parameter types in the calling code.

Creating implicit conversions

For example, the type Timer, which contains hours and minutes, is converted into minutes as binary-long using the following conversion operator:

operator-id Implicit (a as type Timer) returning b as binary-long.
 
  set b to a::Hour * 60 + a::Minutes
end operator.

This operator can then be used in statements such as:

set myMins to timer3 

Similarly, you can convert from a binary-long data item (representing a number of minutes) to the type Timer, which is expressed in hours and minutes, using the following conversion operator:

operator-id Implicit (a as binary-long) returning b as type Timer.

  set b to new Timer
  declare hour as binary-long = a / 60
  set b::Hour to hour
  set b::Minutes to a - 60 * hour  
end operator.

This operator can then be used in statements such as:

set timer4 to myMins

Implicit conversions can occur in all sorts of places, such as assignments and member invocations.

You can invoke an implicit conversion implicitly or explicitly. For example, the following statements both invoke the implicit conversion:

set myMins to timer3 
set myMins to timer3 as binary-long

You use implicit conversions where the conversion is reliable, where it will not lose information and will not throw an exception.

Creating explicit conversions

operator-id Explicit (a as String) returning b as type Timer.
01 strH String.
01 strM String.
01 colonPos  binary-long value 0.
 
  set b to new Timer()
  set colonPos to a::IndexOf(":")
  try
    set strH to a::Substring(0 colonPos)
    set strM to a::Substring(colonPos + 1 2)
    set b::Hour to type Int32::Parse(strH)
    set b::Minutes to type Int32::Parse(strM)
  catch
    display "Invalid time format"
  end-try
end operator.

Explicit conversions occur only in statements that explicitly state the type to convert to. For example, where the conversion operator is defined as explicit, only the second statement below works:

set timer4 to myString                *> fails to compile as no implicit conversion exists 
set timer4 to myString as type Timer  *> succeeds

You use explicit conversions (as opposed to implicit ones) where the conversion has the potential to lose information or throw an exception. In the above example, the string might contain text or be invalid somehow. In this case, a try-catch block is required to handle the failure.