データ型の操作

データ型の操作には次の方法があります。

式が指定されたデータ型を持つかどうかの確認

要約:
isa 演算子を使用して式に指定されたデータ型が含まれているかどうかを判定します。
指定された式が指定されたデータ型を含むかどうかを判定するには、 isa 演算子を使用します。isa 演算子の構文は次のとおりです。
構文:expr isa type
説明:
exprtype と比較するデータ型の式です。
typeexpr の型と比較するデータ型です。
expr のリテラル値を指定すると、この演算子はリテラル値が type 型の変数に格納できる場合、表示を変更せずに true を返します。それ以外の場合は false を返します。次に例を示します。

例: isa とリテラルの使用
98 isa int... {value 98 isa int}

98 isa int8... {value 98 isa int8}

98 isa int32... {value 98 isa int32}

98 isa int64... {value 98 isa int64}

98 isa float... {value 98 isa float}

98 isa double... {value 98 isa double}

98 isa char... {value 98 isa char}
expr がプリミティブ型の変数である場合、この演算子は変数のデータ型が type またはその型の別名であれば true を返します。それ以外の場合は false を返します。次に例を示します。

例: isa とプリミティブ型変数の使用
98 (an int) isa int...
{value
    let x:int = 98
    x isa int
}

98 (an int) isa int32...
{value
    let x:int = 98
    x isa int32
}

98 (an int8) isa int...
{value
    let x:int8 = 98
    x isa int
}
expr がクラス型の変数である場合、この演算子は変数のデータ型が type または type のサブクラスのいずれかであれば true を返します。それ以外の場合は false を返します。次に例を示します。

例: isa とクラス型変数の使用
{define-class Animal}
{define-class Cow {inherits Animal}}

{let harriet:Cow = {Cow}}

harriet isa Cow is ... {value harriet isa Cow}

harriet isa Animal is ... {value harriet isa Animal}

harriet isa double is ... {value harriet isa double}
詳細については、『API リファレンス マニュアル』の isa または 「リレーショナル演算子」 を参照してください。
注意: 実行時に式が可能なデータ型の1つが含まれているかをチェックするには、 isa 式で繰り返しテストする代わりに、 以下のセクションで説明されているように、1つの type-switch を使用するほうが効率的な場合があります。

type-switch

要約:
  • 値のデータ型を使用してコードのどのブロックを実行するのかを判定します。
type-switch 式は値のデータ型を使用してコードのどのブロックを実行するのかを判定します。

例えば、 any 変数値(例:intdoubleDistance)によって、 異なるブロックを実行することができます。
type-switch 式の構文を次に示します。
構文:{type-switch value
 [case identifier-1:type-1 do     code-block-1]
 [case identifier-2:type-2 do     code-block-2]
 ...
 [else code-block-n]
}
解説:
valueコードのどのブロックを実行するのかを判断するデータ型を持つ値です。
type-1, type-2, ...value を各 case句と比較するデータ型です。
identifier-1, identifier-2, ...valueの取りうる各値に対する変数名です。これらの変数名は These variable case 句を伴うコード ブロックで役立ちます。
code-block-1, code-block-2, ...case 句について実行するコード ブロックです。
Curl® 実行環境 は type-switch 式を検出すると、value のデータ型を、type-1type-2、以下同様にそれぞれと比較します。isa 演算子を使用して比較を実行します。true を返す最初の比較を検出すると、対応するコード (後ろに do 式を伴うコード) を実行し、type-switch 式の最後に移動します。true を返す比較が無く else 句もない場合、else 句に続くコードを実行します。else 句がない場合、type-switch 式の直後の式に移動します。
case 句の 1 つのデータ型が value のデータ型と一致する場合、type-switch 式は実行するコード ブロック内の最後の式の値を返します。それ以外の場合、type-switch 式は値を返しません。
たとえば、次の type-switch 式は、実行するコードを判定するための some-function プロシージャへの呼び出しの戻り値のデータ型を使用します。case 句は戻り値についてそれぞれが取りうるデータ型を表します。プロシージャ コールの戻り値の型と一致する最初の case 句のコードを実行します。各 case 句の識別子には、返された値が含まれていることに注意してください。これらは、case 句を伴うコードで役立ちます。また、この場合、else 句はエラーをスローすることにも注意してください。
{type-switch {some-function}
    case i:int do
        {output i, " is an int"}
    case f:float do
        {output f, " is a float"}
    case d:Distance do
        {output d, " is a Distance"}
    case str:String do
        {output str, " is a String"}
    else
        {error "unexpected return value"}
}

式のデータ型の取得

要約:
  • type-of 式を使用して式の実行時のデータ型を判断します。
指定した式のデータ型を判定するには、type-of 式を使用します。type-of 式の構文は次のとおりです。
構文:{type-of expr}
説明:
expr値、変数、または式を代入できます。
type-of 式は式の実行時のデータ型を返します。式の実行時データ型は式のコンパイル時のデータ型とは異なることがあることに注意してください。たとえば、any データ型を使用して変数を宣言する場合、any の compile-time データ型になります。ただし、変数の実行時データ型はその変数に代入された値によって異なります。同様に、クラスを使用して変数を宣言し、サブクラスのインスタンスを変数に代入する場合、実行時データ型はそのサブクラスのデータ型になります。
次の例はプリミティブ データ型の type-of 式のサンプルを示します。

例: type-of とプリミティブ型変数の使用
{let i:int = 3}
The data type of i is... {type-of i}

{let c:char = 'a'}
The data type of c is... {type-of c}
次の例は any データ型の type-of 式のサンプルを示します。type-of 式は変数の実行時データ型を返すことに留意してください。

例: type-ofany 型変数の使用
{let a:any = 3}
The data type of a is... {type-of a}

{let b:any = 'a'}
The data type of b is... {type-of b}
次のサンプルはクラス型変数のある type-of を使用します。type-of 式は式の実行時データ型を返すことに留意してください。

例: type-of とクラス型変数の使用
|| Declare a variable "c" to be of type "VBox".
 || Initialize "c" with a "VBox" object.
 {let c:VBox = {VBox}}
 The data type of c is... {type-of c}

 || Declare a variable "d" to be of type "Box".
 || Initialize "d" with a "VBox" object.
 || Note that "Box" is a superclass of "VBox".
 {let d:Box = {VBox}}
 The data type of d is... {type-of d}
変数の compile-time バージョンを取得するには、実行時型とは逆の compile-time-type-of 演算子を使用します。これは高度な機能です。
type-of を使用して任意の式のデータ型をクエリすることもできます。簡単な例を次に示します。

例: type-of と式の使用
The data type of 3 is ... {type-of 3}

The data type of 3 * 7.0 is ... {type-of (3 * 7.0)}

The data type of 'a' is ... {type-of 'a'}

The data type of "xyz" is ... {type-of "xyz"}

The data type of {StringBuf "xyz"} is ... {type-of {StringBuf "xyz"}}
       
注意: 既定のオブジェクト値 null については、{type-of null}Null を返します。

サブタイプのリレーションシップの確認

要約:
  • サブタイプ値はスーパータイプ値の変数に変換して保存することが出来ます。
  • Type.subtype-of? メソッドを使用して型が他のサブタイプであるかどうかを 判定します。
データ型は、その値が表示を変更せずに第 2 型で宣言された変数に格納される場合、 第 2 データ型のサブタイプです。データ型は、第 2 データ型が第 1 データ型の サブタイプである場合、第 2 データ型のスーパータイプです。具体的には次のようになります。
データ型が第 2 データ型のサブタイプであるかどうかを判断するには、Type クラスの subtype-of? メソッドを使用します。

例: subtype-of? メソッドを使用したサブタイプのテスト
String subtype of String: {(String asa Type).subtype-of? String}

String subtype of #String: {(String asa Type).subtype-of? #String}

#String subtype of String: {(#String asa Type).subtype-of? String}

String subtype of Object:  {(String asa Type).subtype-of? Object}

String subtype of any:  {(String asa Type).subtype-of? any}

int subtype of int64: {int.subtype-of? int64}

データ型の変換

要約:
  • ケースによっては、値の暗黙の変換がサポートされます。
  • asa 演算子を使用して値を明示的に変換できます。
  • 任意の値を、暗黙のコンストラクタまたはファクトリのマッチングをサポートする クラスに変換できます。
  • プリミティブ型の値は通常他のプリミティブ型にのみ変換できます。
  • クラス型の値は他のクラス型にのみ変換できます。
ケースによっては、値を他のデータ型に変換できることがあります。あるデータ型の値を異なるデータ型の変数に代入すると、変数への配置が試行されます。たとえば、文字を整数変数に代入する場合、その文字の数値が変数に代入されます。こうした変換は値を変換する式を指定しないので、 暗黙の 変換とよばれます。これらの変換は自動的に実行されます。文字が同じ名前を割り当てて列挙型要素に変換されるとき、暗黙の変換の他のインスタンスが行われます。(「列挙型の変数」のセクションを参照してください。)
暗黙の変換は通常、代入ステートメントで値を関数に渡すときに行われます。あるデータ型の値を異なるデータ型の変数に代入すると、暗黙の変換を実行しようとします。同様に、あるデータ型の値を異なるデータ型の引数に渡す場合、暗黙の変換を実行しようとします。ただし、変換でデータの失われる可能性がある場合、暗黙の変換は実行されません。たとえば、intint8 に変換する場合、int の値が int8 の値の有効範囲外になることがあるためデータを失う可能性があります。データを失う可能性のあるデータ型に値を暗黙で変換しようとすると、Curl® コンパイラは構文エラーを発生させます。
asa 演算子を使用して値をあるデータ型から他のデータ型へ明示的に 変換することができます。 asa 演算子には以下の構文があります。
構文:expr asa type
説明:
  • exprは、変換する型の式です。
  • type は、変換先 expr のデータ型です。
次に例を示します。

例: asa の使用
{value
    || Declare a variable "d" with data type double and
    || initialize the variable with the value 37.7 as
    || a double-precision floating-point number.
    let d:double = 37.7

    || Display the value of the variable "d" converted
    || to an int.
    d asa int
}
コンパイル時に検出された変換エラーは SyntaxError になりますが、それらの検出されたエラーは後で実行時に CastException になります。暗黙と明示のどちらの変換を実行するのかを決定するとき、実行時ではなくコンパイル時に型チェックを行うのが望ましいということを考慮する必要があります。値を明示的に変換する場合、Curl® コンパイラが不正な変換をキャッチすることが多くなります。
プリミティブ データ型とクラス型で変換の規則は異なります。一般に、プリミティブ データ型の値はクラス型の値に変換できません。その逆も同様です。ただし、値で起動される暗黙のコンストラクタまたはファクトリーをサポートしているクラスには変換できます。この機能の詳細については、implicit キーワードのドキュメントを参照してください。

プリミティブ データ型の変換

要約:
  • 拡大変換では、保持できる情報が多いデータ型への変換になります。
  • 縮小変換では、保持できる情報が少ないデータ型への変換になります。
  • char 間の変換のような、変換もサポートされています。
  • すべての数値型の間で変換できるわけではありません。たとえば、intdouble にキャストできますが、DistanceTime にキャストできません。
拡大変換にはより多くの情報を保持するデータ型への値の変換が含まれます。 つまり、変換によるデータの損失は発生しません。拡大変換の例では、int8int16 に変換します。拡大変換は暗黙または明示のいずれかで実行できます。
Figure: 拡大変換
縮小変換には、ある型から保持できる情報が少ないデータ型へのデータの変換が含まれます。 つまり、変換によるデータの損失が発生する可能性があります。縮小変換の例では、int16int8 に変換します。int16 の値が int8 の取りうる値の範囲外である場合、 データを損失する可能性があります。値を明示的に変換して縮小変換を実行する必要があります。
Figure: 縮小変換
プリミティブ データ型では拡大変換及び縮小変換の両方が実行できますが、例外があります。bool データ型への変換、および数量へのあるいは数量からの変換はできません。 bool を明示的なキャストで整数型に明示的変換ができます。(この機能は 6.0 で追加されました。)明示的なキャストにより、 truefalse はそれぞれの指定された整数型の1と0に変換されます。次に示すような、拡大変換と縮小変換のカテゴリーに収まらない変換もあります。
数値データ型を char に変換する際、そのデータは最初に自動的に整数値に変換されます。 整数値は char の正当値 char である必要があります。 そうでなければ、エラーが発生することになります。  char の正当値は以下の範囲になります:
整数と浮動小数点データ型で変換するときは、データを損失することがあるので注意する必要があります。浮動小数点数から整数への変換中に、数の小数部分が切り捨てられます。たとえば、double 型の値 37.7int 型に変換される場合、その値は 37 に変更されます。

例: 整数と浮動小数点データ型の変換
{value
    || Declare a variable "i" with the data type int.
    let i:int
    || Assign the value 37.7 to the variable "i".
    || The value 37.7 is explicitly converted to an
    || integer value.
    set i = 37.7 asa int
    || Display the value of "i".
    i
}
大きな整数値を浮動小数点データ型の変数に代入する場合、コンピュータによっては浮動小数点数を格納する方法により、情報が失われることがあります。次に例を示します。

例: 変換中の情報の損失
{value
    || Declare a variable "f" with the data type float.
    let f:float
    || Assign the value 2147483525 to the variable "a".
    || The value 2147483525 is implicitly converted to
    || a single-precision floating-point value.
    set f = 2147483525
    || Display the value of "f" as an int.
    f asa int
}
プリミティブ データ型間で実行できない唯一の変換は、bool 間または数量データ型の変換です。つまり、bool 値は他のプリミティブ データ型に変換できません。その逆も同様です。同様に、4mなどの数量を int のような他のプリミティブ データ型、または Time のような互換性のない数量型に変換することはできません。
符号付き整数と符号なし整数データ型で変換するときにも、データを損失することがあるので注意する必要があります。符号付き整数から符号なし整数への変換では、負の数が予想通りに変換されないことがあります。これは、符号なし整数の内部的な表示方法のためです。
Figure: 符号付きデータ (int8) と 符号なしデータ (uint8)の内部構造
Figure: 符号付きデータと符号なしデータの内部マッピング
次のインタラクティブな例は、int8 から uint8 への変換を示します。

例: 縮小変換の実行
{value -128 asa uint8}
{value -64 asa uint8}
{value 0 asa uint8}
{value 64 asa uint8}
{value 127 asa uint8}
もちろん、これらの原則は uint8 から int8 への逆変換にも適用されます。さらに同様の原則が符号付きと符号なしデータ型間のすべての変換に適用されます。
プリミティブ データ型間のすべての変換を記載した表については、「プリミティブ データ型変換」を参照してください。

クラス型の変換

要約:
  • アップ キャスティングでは、オブジェクトをスーパークラスの型に変換します。
  • 暗黙または明示的にオブジェクトをアップ キャスティングできます。
  • アップ キャスティングすると、スーパークラスのメンバにだけアクセスできます。
  • オブジェクトの変換時に情報が失われることはありません。
  • ダウン キャスティングはオブジェクトをサブクラスの型に変換します。
  • 明示的にのみオブジェクトをダウン キャスティングできます。
オブジェクトをスーパークラスまたはサブクラスのクラス型に変換できます。ただし、そのオブジェクトのクラス継承ツリーにないクラス型へオブジェクトを変換することはできません。
スーパークラスのクラス型へのオブジェクトの変換は、アップ キャスティングとよばれます。つまり、アップ キャスティングはクラス継承ツリーをスーパークラスの方へ遡ります (クラス継承ツリーの根は通常その最上部にあり、その下に枝葉があるのでアップという語が使用されます)。
Figure: アップキャスティング
アップ キャスト変換は暗黙または明示のいずれかで実行できます。アップ キャスティングの例では、CommandButtonGraphic への変換を行います。ここで、CommandButtonGraphic のサブクラスです。
let foo:Graphic = {CommandButton}
クラスはそのスーパークラスのすべてのクラス メンバを継承します。また、通常はそれ自身のクラスメンバを持っています。オブジェクトをスーパークラスの型にアップ キャストするときは、スーパークラスのメンバにだけアクセスできます。つまり、元のクラス内だけで定義されているメンバにはアクセスできません。
Figure: アップ キャスト クラスはスーパークラスのメンバにだけアクセス可能
ただし、元のクラス内でだけ定義されているメンバにある情報は失われないことを知っておく必要があります。オブジェクトがスーパークラスのクラス型を持っている間はこの情報にアクセスできませんが、メモリには残っています。情報にアクセスするには、オブジェクトを元のクラスの型に変換しなおします。次に例を示します。
foo asa CommandButton
サブクラスの型へのオブジェクトの変換を ダウン キャスティングとよびます。つまり、ダウン キャスティングではクラス継承ツリーをサブクラスへ向かって下方へ移動します。アップ キャスティングは事前に行われているので、通常はダウン キャスティングすることになります。Curl 言語では、オブジェクトを暗黙にダウン キャストすることはないので、asa 演算子を使用して明示的にオブジェクトを変換する必要があります。
Figure: ダウン キャスティング
これらの概念を補強するには、TextFieldBaseFrame のサブクラスになっている次の例を検討してください。コード内では、代入ステートメントは暗黙に TextFieldBaseFrame に変換します。

例: TextField から BaseFrame への暗黙のアップ キャスティング
|| Declare a variable "b" of type BaseFrame.
{let b:BaseFrame = {BaseFrame}}

|| Declare a variable "t" of type TextField, a subclass of BaseFrame.
{let t:TextField = {TextField value = "hello"}}

|| Assign a TextField value to a BaseFrame variable.
|| The value of t is upcast to the BaseFrame type.
{set b = t}

|| Display the object that "b" now refers to
{value b}

|| Check the runtime and compile-time types of "b"
b's runtime type is ... {type-of b},
but its compile-time type is ... {compile-time-type-of b}
TextField クラスには value アクセッサが含まれます。ただし、BaseFrame クラスには value プロパティはありません。そのため、上記の例で f オブジェクトの value プロパティを取得しようとすると、コンパイラは構文エラーを発生させます。
|| Display the value of the object.
b.value
--> 構文エラー!
value ゲッターを使用するには、オブジェクトを明示的に TextField に変換する必要があります。次に例を示します。

例: BaseFrame から TextField への明示的なダウンキャスティング
{value
    || BaseFrame is a superclass of TextField
    let b:BaseFrame = {BaseFrame}
    let t:TextField = {TextField value = "hello"}

    || A TextField value is implicitly upcast to the BaseFrame type.
    set b = t

    || Explicit downcasting back to a TextField makes this operation possible.
    (b asa TextField).value
}