Managed COBOL distinguishes between:
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.
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:
During boxing, the system copies the value into the object heap and returns a reference to it. When the reference is no longer active (because nothing in the program is holding on to it), the space in the object heap will eventually be regained by the garbage collector.
See the ValueTypes sample, available from Start > All Programs > Micro Focus Studio Enterprise Edition > Samples and under COBOL for .NET.
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:
imperative-clause TYPE OF object
For example, in .NET COBOL:
*> Type information 01 x binary-long. display x::GetType *> Prints System.Int32 display type of binary-long *> Prints System.Int32 display x::GetType::Name *> Prints Int32
For example, in JVM COBOL:
*> Type information 01 x binary-long. display x::getClass *> Prints System.Int32 display type of binary-long *> Prints System.Int32 display x::getClass::Name *> Prints Int32
A conversion operator converts a data item from one data type into another, such as from a type that you have defined into a integer. Conversion operators can be overloaded, so that the appropriate conversion operator is used according to the parameter types in the calling code.
For example, the type Timer, which contains hours and minutes, is converted into minutes as binary-long using the following conversion operator:
set myMins to timer3 ...
operator-id Implicit. procedure division using value a as type Timer returning b as binary-long. set b to a::pHour * 60 + a::pMins end operator.
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:
set timer4 to myMins ...
operator-id Implicit. procedure division using value a as binary-long returning b as type Timer. set b to new Timer if a >= 60 set b::pHour to 1 set b::pMins to a - 60 else set b::pMins to a end-if end operator.
You can define explicit conversions, in addition to implicit conversions. They differ as follows:
set timer4 to myString *> fails set timer4 to myString as type Timer *> succeeds
operator-id Explicit. 01 strH type String. 01 strM type String. 01 colonPos binary-long value 0. procedure division using value a as String returning b as Type Timer. 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::pHour to type Int32::Parse(strH) set b::pMins to type Int32::Parse(strM) catch display "Invalid time format" end-try end operator.
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.
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.
You can cast an object reference to an instance of another type using AS. The Compiler can then check whether the operation is type safe or not.
In the following example, we know that obj-object points to a System.AppDomain reference so we know we can cast up the class hierarchy from System.Object to System.AppDomain.
*> set obj-app-domain to obj-object *> fails if uncommented set obj-app-domain to obj-object as type System.AppDomain *> succeeds
Casts can be inline, so you can do the following:
display obj-object as type System.AppDomain::FriendlyName
A cast is only legal if the target type (the type being cast to) derives directly from the source type, or vice versa. In the following statement a System.Type reference (obj-type) can never point to a System.AppDomain instance because the classes are unrelated in the hierarchy.
*>set obj-app-domain to obj-type as type System.AppDomain *> fails if uncommented
If the cast fails, a System.InvalidCastException is thrown by the CLR. To avoid this, either test for a valid cast first using INSTANCE OF or wrap the cast in an exception block (see exception handling example for more information).
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