Section 2.9 多重継承(その2)

今回は多重継承の続きね。
前回はスーパークラスになる DictionarySaver クラスDictionaryLoader クラスを作ったんだったよね。
そうそう。
じゃあ早速この2つのクラスを継承して、保存と読み込みが両方できる DictionaryIO クラスを作ってみるね。
うん。

<多重継承の例:DictionarySaver クラスと DictionaryLoader クラスを継承している DictionaryIO クラス>

class DictionaryIO extends DictionarySaver, DictionaryLoader
{
    // コンストラクタ
    function DictionaryIO()
    {
        DictionarySaver();   // DictionarySaver クラスのコンストラクタを呼び出します
        DictionaryLoader();  // DictionaryLoader クラスのコンストラクタを呼び出します
    }

    // デストラクタ
    function finalize()
    {
        global.DictionarySaver.finalize();   // DictionarySaver クラスのデストラクタを呼び出します
        global.DictionaryLoader.finalize();  // DictionaryLoader クラスのデストラクタを呼び出します
    }

    // DictionarySaver クラスの save メソッドをオーバーライドしています
    // 第1引数:保存する辞書配列
    // 第2引数:保存するファイル名(省略した場合はファイル選択ダイアログを表示します)
    // 戻り値はありません
    function save(dic, fileName = "")
    {
        if(fileName == "")
        {
            // 第2引数が省略されている場合はファイル選択ダイアログボックスを表示します
            var sel = new FileSelector();
            if(!sel.saveFile())
                return;  // キャンセルされた場合は何もせず return します

            fileName = sel.name;
        }

        // DictionarySaver クラスの save メソッドを呼び出します
        global.DictionarySaver.save(dic, fileName);
    }

    // DictionaryLoader クラスの load メソッドをオーバーライドしています
    // 第1引数:読み出すファイル名(省略した場合はファイル選択ダイアログを表示します)
    // ファイルが存在していればファイルから読み出した辞書配列を返します
    // ファイルが存在しなければ void を返します
    function load(fileName = "")
    {
        if(fileName === "")
        {
            // 引数が省略されている場合はファイル選択ダイアログボックスを表示します
            var sel = new FileSelector();
            if(!sel.openFile())
                return void;  // キャンセルされた場合は void を返します

            fileName = sel.name;
        }

        // DictionarySaver クラスの load メソッドを呼び出します
        return global.DictionaryLoader.load(fileName);
    }
}

まず最初の行から。
多重継承する場合は、extends の後に『,』で区切ってスーパークラス名を並べて書くんだ。
これはわかりやすいね。
うん。じゃあ次はコンストラクタね。
コンストラクタでは全部のスーパークラスのコンストラクタを呼び出す必要があるんだ。
あれ? super がついてないよ?
多重継承する場合は super キーワードは使えないんだ。
えっ、そうなの?
うん。だってスーパークラスが2つ以上あったら、super だけだとどのスーパークラスかわからないでしょ。
あ、そっか。
でも super をつけなくてもスーパークラスのコンストラクタって呼び出せるの?
コンストラクタの場合は名前がクラスごとに違うから super をつけなくても呼び出せるよ。
あ、そういえば名前がかぶってなかったら super をつけなくてもいいんだったね。
ん。で、問題なのはデストラクタ
デストラクタは名前が finalize って決まってるから、そのままじゃ呼び出せないね。
そ。だから global キーワードを使って呼び出すんだ。
global キーワード?
global キーワードを使うと、クラスとか、クラスのメソッドじゃない関数とか、 すべてのブロックの外側で宣言されてる変数とかが登録されてる global オブジェクトって言うオブジェクトにアクセスできるんだ。
???
簡単に言うと、『global.クラス名.メソッド名(引数);』って書けば、多重継承してる時でもスーパークラスのメソッドを呼び出せるってこと。
あ、そうなんだ。
だから DictionarySaver クラスのデストラクタは global.DictionarySaver.finalize(); で呼び出せて、 DictionaryLoader クラスのデストラクタは global.DictionaryLoader.finalize(); で呼び出せるんだね。
ん、そういうこと。
これでコンストラクタとデストラクタは解った?
うん、おっけーだよ。
じゃあ、後は save メソッドload メソッドね。
save メソッドと load メソッドはオーバーライドしてるんだよね?
うん、そう。
両方ともオーバーライドして機能を追加したんだけど、どこが変わったかわかる?
えっと、どっちも fileName 引数のデフォルトが空文字列になってるね。
ん。2つとも fileName を省略可能にしたんだ。
fileName が指定されてる場合はスーパークラスの save, load メソッドを呼び出すだけみたいだけど、 fileName が省略されてたり空文字列だったりすると、最初に作った FileSelector クラスを使って、ファイル選択のダイアログボックスを表示するようにしてるんだよね?
うん、そういうこと。
で、選択されたファイル名をスーパークラスのメソッドに渡してるんだ。
スーパークラスのメソッドを呼び出す時に global キーワードを使わなくちゃいけないこと以外は、 普通の継承のオーバーライドといっしょだね。
うん。
じゃあ、DictionaryIO クラスを実際に使ってみよっか。
そだね。
それじゃ、まずは保存からね。

<辞書配列の中身を保存するスクリプト>

var dio = new DictionaryIO();
var dic = new Dictionary();

dic.void = void;     // 要素 void には void を代入します
dic.int = 1;         // 要素 int には整数 1 を代入します
dic.real = 0.5;      // 要素 real には実数 0.5 を代入します
dic.string = "abc";  // 要素 string には文字列 "abc" を代入します

dio.save(dic);  // 辞書配列をファイルに保存します

invalidate dio;

今回もスクリプトが長くなっちゃってるから、ファイルにまとめといたよ。
それじゃ、実行してみて。
うん!

「名前を付けて保存」ダイアログボックス

第2引数を省略してるから、ファイルの選択ダイアログボックスが表示されたね。
適当なファイル名で保存してみて。
テキスト形式で保存されるから拡張子は txt でいいと思うよ。
わかった。じゃあ、dic.txt って名前にして、保存っと。
これで辞書配列の中身が保存されたはずだから、dic.txt をメモ帳とかで開いてみて。
は〜い!

dic.txt の中身>

dic.txt の中身

ね、ちゃんと辞書配列になってるでしょ。
ほんとだ。
でも、『"int" => int 1』ってなってるけど、int 1int って何?
それは int 演算子だね。
int 演算子…?
§1.6 で説明したよね。
あっ、int 型に変換する演算子だね。
そ。同じように、実数型の値には real 演算子が使われてて、 文字列型の値には string 演算子が使われてるよね。
void はそのまんま void だけどね。
そっか、void 以外は全部演算子が使われてるんだね。
あ、でも実数型の値は『0x1.0000000000000p-1』っていう見たことない書き方になってるよ?
これは 1.0×2-1、つまり 0.5 を16進数で表現した書き方なんだ。
16進数?
ここでは詳しく説明しないけど、KAG の position タグで、メッセージレイヤの色を指定する時とかに使う書き方だよ。 あと HTML で色を指定する時とかにも使うね。
あー、あの書き方ね。
でもなんか見た目が違う気がするんだけど…
position タグとかだと整数しか扱わないからね。
辞書配列を保存する時には実数を16進数で表現するから違って見えるんだと思うよ。
ふーん、そうなんだ。
じゃあ今度はこのファイルを読み込んでみよっか。
うん。

<辞書配列の中身を読み出すスクリプト>

var dio = new DictionaryIO();
var dic = dio.load();

if(dic !== void)
{
    // 辞書配列を読み込めた場合は、その中身を表示します
    System.inform("要素名:void\nデータ型:" + (typeof dic.void) + "\n値:" + dic.void);
    System.inform("要素名:int\nデータ型:" + (typeof dic.int) + "\n値:" + dic.int);
    System.inform("要素名:real\nデータ型:" + (typeof dic.real) + "\n値:" + dic.real);
    System.inform("要素名:string\nデータ型:" + (typeof dic.string) + "\n値:" + dic.string);
}

invalidate dio;

なんか表示する文字列が変じゃない?
\n』って何のために表示するの?
\n』はエスケープ文字って言って、このまま表示されるわけじゃないんだ。
エスケープ文字?
エスケープ文字っていうのは普通の文字じゃ表せない要素を文字列の中に入れたい時とかに使うんだ。
\n は改行文字だから、文字列の中に \n を書くと、 表示されるメッセージがその位置で改行されるよ。
へぇ、そうなんだ。
それじゃ、実行してみて。
これも ファイルにしといたから。
おっけー。

「ファイルを開く」ダイアログボックス

今回も fileName を省略したからファイル選択ダイアログボックスが表示されたね。
えっと、さっき保存した dic.txt を選べばいいんだよね?
ん。
じゃあ、dic.txt を選択して「開く」だね。

<1つ目のメッセージ>

1つ目のメッセージ

<2つ目のメッセージ>

2つ目のメッセージ

<3つ目のメッセージ>

3つ目のメッセージ

<4つ目のメッセージ>

4つ目のメッセージ

dic.void は値が何も表示されてないね。
void は文字列型に変換すると空文字列になるから、 メッセージとして表示しても何も表示されてないように見えるんだよ。
あ、そういうことね。
うん、じゃあちゃんと全部の要素が読み込まれてるね。
ん、これで OK だね。
それじゃあ、今回はこんなところかな。
今回も本題以外のことを結構色々やったけど、多重継承は解った?
うん、一応解ったよ。
でも、多重継承してクラスを作れって言われると、ちゃんと作れる自信はあんまりないかな…
まぁ、多重継承はそんなに使わないから、多分それくらいで大丈夫だと思うよ。
えっ、そうなの?
大体は普通の継承でなんとかなるからね。
もしくは継承する代わりに、そのクラスのオブジェクトをメンバ変数にしとくって方法もあるしね。
ふ〜ん、そうなんだ。
ん、だから多重継承がどんなものかが解ればとりあえず OK かな。
うん、それなら大丈夫だよ。
んじゃ、これで多重継承の話はおしまい。
これでクラスのメインの部分は説明できたかな。
じゃあ、この章はもう終わり?
ううん、まだちょっとややこしい話が残ってるから、もうしばらく続くよ。
えっ、これ以上ややこしくなるの?
あー、でも TJS にはこんな機能もあるよ、っていう程度だから、そんなに心配しなくてもだいじょうぶ。
あ、そうなんだ。よかった。
うん。
それじゃ、また次回ね。


前へ | TOP | 次へ