今回は AS3 の型システムの特徴についての話です。strict モードの人には関係ない話が続きましたが、ここからはコンパイラの環境設定に依存しない話題です(たぶん)。
まず、下のコードについて考えてみます。 オブジェクト型の変数 foo に数値の 0 を代入して空文字列と比較しています。
var foo:Object = 0; if ("" == foo) { trace("foo は空文字列です"); }
上の If 文は空文字を検出するためのものに見えますが、実際にはこれを実行すると "foo は空文字列です" が表示されます。
この結果は予想通りだったでしょうか?想定外だった人はこの先を。
上記の結果は、AS3 が弱い型の性質を持っているために起こります。AS3 では '+' 等のオペレータに対して複数のデータ型が混在すると処理の前に暗黙の型変換が行われます。これによって例えば trace("count:" + 0) のように文字列と数値を"足して"もそれらしい結果を得ることができるわけです。
暗黙の型変換は上の trace() の例のようにしばしば便利な機能として使われますが、一方で予期せぬ結果になるケースもある面倒なものだったりもします。例えば、この記事の最初に挙げた例も暗黙の型変換の結果によるものですが、実際にどのような変換が行われて true と評価されたのかは直感的には理解し難いものです。
ということで、これから上のサンプル内の if 文で実際に起きていたこと、このような結果を避ける方法について話を移そうと思いますが、その前に 2 つほど基礎となる項目の確認です。
int がオブジェクトであること
とりあえず、下のサンプルをみてください。
var foo:int = 0; var bar:String = foo.toString();
2 行目のように int 型の変数のメソッドが呼びだせるということは、AS3 の int は Java でいうと Integer に近いものだということです。ASDoc を見てみると、実際に int が Object のサブクラスであることが確認できます。同様に uint や Number も Object のサブクラスです (Number はクラス名っぽいですね)。
このため冒頭の例でも Object 型の変数に数値を代入することができたわけです。
ちなみに下の 2 行はプログラムの意味的には同じになります。もちろん動的な属性を持たない int 型のオブジェクトのコンストラクタをわざわざ呼ぶ必要はありませんが。
var i:int = 0; var i:int = new int(0);
「型が無い」という型
数値もオブジェクトとして扱われるということで全ての値は Object 型に属するように思えますが、実際は例外があります。Object は undefined を値として持つことができません。undefined が属性値として必要な場合は * を型として指定します。
AS2 では Object 型オブジェクトの初期値は undefind でしたが AS3 では null になっています。* 型の初期値は undefined です。
ところで * は型指定が無いという型(ちょっと矛盾?)です。というわけで、* は型注釈を省略したときに使われる型でもあります。
// 下の2行は同じ意味 var foo; var foo:*;
変数の型を明示的にしない場合 Object 型ではないことに注意しましょう。* 型のオブジェクトは属性やメソッドを持たないため、コンパイル時の型チェックが行われません。
実行時まで型チェックを遅らせられることは動的言語の利点ですが、厳密な型チェックを行う strict モードでも * を使うとこの恩恵にあずかることができます。
ちょっと長くなったので、続きは次回に。
コメントする