型と型変換

値型、参照型、およびボックス化

マネージ COBOL では、次の型を区別します。

  • 値型 (binary-long や .NET の System.DateTime など)。値型のデータ項目には実際の値が含まれます。たとえば、COBOL binary-long データ項目には 32 ビット整数が含まれます。
  • オブジェクト ヒープで割り当てられる参照タイプ。参照型のデータ項目は、オブジェクト ヒープへの参照を保持します。参照型はガベージ コレクターの管理下にあり、アクティブな参照がなくなった時点でオブジェクト ヒープ上の領域が解放されます。

すべての値型は、ボックス化と呼ばれるプロセスで参照型に変わることがあります。たとえば、オブジェクト参照を、binary-long などの値型に設定できます。ボックス化の際に、値がオブジェクト ヒープにコピーされ、参照がオブジェクト ヒープに戻されます。

ボックス化は、必要に応じて自動で行われます。たとえば、オブジェクトをパラメーターとして予測するメソッドに値型をパラメーターとして渡す場合などです。

明示的にボックス化するには、値型を汎用型 (.NET COBOL の System.Object や JVM COBOL の java.lang.Object など) に割り当てます。値型のボックス化を解除して、元の値型を復元できます。

データ項目の定義で TYPE classname を指定する場合:

  • classname が参照型を表す場合、参照型のオブジェクトを取得します。
  • classname が値型を表す場合、値型のオブジェクトを取得します。

型情報

すべてのクラスにはルート オブジェクトが関連付けられています。このルート オブジェクトを使用して、そのフィールドやメソッドなどのクラスに関する情報を取得できます。型情報を取得するには、次の方法を使用します。

  • .NET COBOL では GetType() - オブジェクト参照の場合は System.Type オブジェクトを取得します。
  • JVM COBOL では getClass() - java.lang.Class オブジェクト参照を取得します。
  • TYPE OF - クラス、インターフェイス、デリゲート、または列挙の System.Type オブジェクトまたは java.lang.Class オブジェクトを取得します。
    imperative-clause TYPE OF type-name
  • TYPE OF type-name[ANY...] - 汎用クラス、インターフェイス、またはデリゲートの System.Type (.NET) オブジェクトまたは java.lang.Class (JVM) オブジェクトを取得します。

たとえば、JVM 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.

型変換

型変換では、ある型の式を別の型に変換できます。型変換には明示変換と暗黙変換の 2 種類があります。明示変換を実行するには、AS 指定または AS IF 指定を使用して変換後の型を指定する必要があります。この種類の変換の例を次に示します。

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

この例では、Animal 型のすべてのオブジェクトが Warthog 型であるわけではないので明示変換が必要です。

暗黙変換の例を次に示します。

Set myAnimal to myWarthog

Warthog 型は Animal 型から派生するため、変換は常に正当なものであり、暗黙的に行うことができます。同様に、暗黙変換を使用して、Animal 型のパラメーターを値によって渡すすべてのメソッドに引数として myWarthog を渡すことができます。

暗黙変換

COBOL では、次の種類の暗黙変換が可能です。

  • 暗黙数値変換
  • 暗黙列挙変換
  • 暗黙参照変換
  • ボックス化変換
  • ユーザー定義の暗黙変換
  • 匿名メソッド変換
  • メソッド グループ変換

暗黙変換が発生する主なコンテキストは次のとおりです。

  • 割り当て (SET 文や DECLARE 文など)
  • パラメーターの引き渡し

暗黙列挙変換

数値リテラル 0 は、どの列挙型にも変換できます。

注: この種類のバージョンは .NET COBOL では許可されますが、JVM COBOL では許可されません。

暗黙参照変換

型 S から型 T への暗黙変換は、次の場合に許可されます。

  • S が T から派生する
  • T がインターフェイス型で、S が T を実装する
  • S と T がどちらも配列であり、次元の数が同じである
    • S と T の配列要素がどちらも参照型である
    • S の要素の型から T の要素の型への暗黙参照変換が存在する

.NET COBOL では次の点に注意してください。

  • 配列型はすべて型 System.Array から派生する
  • 1 次元配列 (型 S[] など) はすべてインターフェイス System.Collections.Generic.IList[S] を実装する
  • デリゲート型はすべて System.Delegate の派生である型 System.MulticastDelegate から派生する

暗黙参照変換の例を次に示します。

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

ボックス化変換

.NET COBOL では、どの値型も System.Object 型または System.ValueType 型のいずれかの参照型に変換できます。また、列挙型は System.Enum 型に変換できます。

JVM COBOL では、プリミティブ型をボックス化された型に次のように変換できます。
  • binary-char は java.lang.Byte に変換できる
  • binary-short は java.lang.Short に変換できる
  • binary-long は java.lang.Integer に変換できる
  • binary-double は java.lang.Long に変換できる
  • condition-value は java.lang.Boolean に変換できる
  • character は java.lang.Character に変換できる
  • float-short は java.lang.Float に変換できる
  • float-long は java.lang.Double に変換できる

ユーザー定義の暗黙変換

型 S1 から型 T1 への暗黙的なユーザー定義型変換演算子 (下記参照) が存在する場合、型 S から型 T へのユーザー定義の暗黙変換が存在します。次に例を示します。

  • S が S1 と同じであるか、S から S1 への非ユーザー定義の変換が存在する
  • T が T1 と同じであるか、T1 から T への非ユーザー定義の変換が存在する

匿名メソッド変換

シグネチャ S を持つ匿名メソッドは、同じシグネチャと戻り値の型を持つ任意のデリゲート型に変換できます。また、パラメーターのない匿名メソッドは、同じ戻り値の型を持つ任意のデリゲート型に変換できます。

メソッド グループ変換

メソッド名が method-name であるメソッド グループで、シグネチャが S で戻り値の型が R のデリゲート型に変換できるのは、そのメソッド グループ内に同じシグネチャと戻り値の型を持つメソッド (つまり、指定された名前のメソッド) がある場合です。

明示変換

明示変換は AS [ IF ] 指定を使用して指定します。変換後の型の式が許可される場所であればどこでも使用できます。

COBOL では、次の種類の明示変換が可能です。

  • 数値変換を含むすべての暗黙変換
  • 明示列挙変換
  • 明示参照変換
  • ボックス化解除変換
  • ユーザー定義の明示変換

明示列挙変換

どの列挙型も任意の数値型に明示的に変換できます。

どの数値型も任意の列挙型に明示的に変換できます。

明示参照変換

型 S から型 T への明示変換 (キャストとも呼ばれる) は、次の場合に許可されます。

  • 型 T が型 S から派生する
  • 型 S がクラス型で型 T がインターフェイス型であり、S が T を実装せず、S が最終でない
  • 型 S がインターフェイス型で型 T がクラス型であり、T が最終でないか T が S を実装する
  • S と T がどちらもインターフェイス型であり、S が T から派生したものでない
  • S と T がどちらも配列型であり、次元の数が同じである
    • S と T の配列要素がどちらも参照型である
    • S の要素の型から T の要素の型への明示参照変換が存在する

キャストに失敗すると、例外がスローされます (.NET の場合は InvalidCastException、JVM の場合は ClassCastException)。これを避けるには、INSTANCE OF を使用して有効なキャストかどうかを最初にテストするか、例外ブロックでキャストをラップします (詳細は「TRY (試行) 文」を参照)。または、AS IF 指定を使用して、無効なキャストの場合はターゲット オブジェクトを null に設定して例外をスローしないように設定します (詳細は「」を参照)。

明示参照変換の例を次に示します。
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
実際に obj-object が System.Type インスタンスを参照している場合、obj-object が System.AppDomain インスタンスを参照すると断言すると、例外が次のようにスローされます。
*> these statements fail if uncommented  
*> set obj-object to obj-type                 
*> set obj-app-domain to obj-object as type System.AppDomain

ボックス化解除変換

.NET COBOL では、System.Object 型または System.ValueType 型の項目を明示的に値型に変換できます。System.Enum 型の項目は列挙型に変換できます。

次に例を示します。

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
JVM COBOL では、次の型をプリミティブ型に変換できます。
  • java.lang.Byte は binary-char に変換できる
  • java.lang.Short は binary-short に変換できる
  • java.lang.Integer は binary-long に変換できる
  • java.lang.Long は binary-double に変換できる
  • java.lang.Boolean は condition-value に変換できる
  • java.lang.Character は character に変換できる
  • java.lang.Float は float-short に変換できる
  • java.lang.Double は float-long に変換できる

ユーザー定義の明示変換

型 S1 から型 T1 への明示的なユーザー定義型変換演算子 (下記参照) が存在する場合、型 S から型 T へのユーザー定義の明示変換が存在します。

  • S が S1 と同じであるか、S から S1 への非ユーザー定義の明示変換が存在する
  • T が T1 と同じであるか、T1 から T への非ユーザー定義の明示変換が存在する

ユーザー定義変換演算子の作成

変換演算子は、あるデータ型 (整数に定義した型など) のデータ項目を別のデータ型に変換します。変換演算子はオーバーロードできるため、適切な変換演算子が呼び出しコードのパラメーター型に従って使用されます。

暗黙変換の作成

たとえば、時間と分を含むタイプ Timer は、次の変換演算子を使用して binary-long として分に変換されます。

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

この演算子は、次のような文で使用できます。

set myMins to timer3 

同様に、次の変換演算子を使用して、binary-long データ項目 (分を表す) から、時間と分で表されるタイプ Timer に変換できます。

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.

この演算子は、次のような文で使用できます。

set timer4 to myMins

暗黙変換は、割り当てやメンバーの呼び出しなどのあらゆる場所で行われます。

暗黙変換は暗黙的または明示的に呼び出すことができます。たとえば、次の文はどちらも暗黙変換を呼び出します。

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

変換が信頼できる場合 (情報が失われず、例外がスローされない場合)、暗黙変換を使用します。

明示変換の作成

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.

明示変換は、変換後の型を明示的に提示する文でのみ行われます。たとえば、変換演算子が明示として定義される場合、次の 2 番目の文のみが機能します。

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

変換で情報が失われたり、例外がスローされる可能性がある場合、(暗黙変換ではなく) 明示変換を使用します。上記の例では、文字列にテキストが含まれるか、何らかの理由で文字列が無効になります。この場合、失敗を処理するには try-catch ブロックが必要です。