継承するときサブクラスで値の橋渡しが必要
AS2で、継承するときの細かい挙動についてあいまいだったでいまさらながら検証してみました。
プロパティの継承
サブクラスで再定義されたプロパティは上書きされ、スーパークラスのものは同じようにアクセスできる
// A.as class A { private var _a:String= "A"; private var _b:String= "A"; function A() { trace( _a +" : "+ _b ); } }
// B.as class B extends A { private var _a:String = "B"; }
// Timeline new A(); // trace-> A : A new B(); // trace-> B : A
この場合、コンストラクタを再定義する必要がありません。
コンストラクタに引数がある場合
Aのコンストラクタに引数を定義します。
// A.as class A { private var _a:String= "A"; function A( p:String ) { trace( _a +" : "+ _b +" : "+ p ); } }
// B.as class B extends A { private var _a:String = "B"; }
// Timeline new B("hoge"); // trace-> B : A : undefined
というふうに、引数がうまく継承されません。
Bクラスを次のように変えるとうまくいきます。
// B.as class B extends A { private var _a:String = "B"; function B( p ) { super( p ); } }
こういう部分が非常にあいまいだったのでスッキリしました。
さらに、仕組みとしてしっかり理解するために
現象としてはこれでスッキリするんですが、仕組みとしてどういう規則があるのかいまいちわかりませんので、自分で租借しなおすためにも内部的にどういう意味になるのか調べてみました。
不思議なのは、継承の仕組みとしてプロトタイプチェーンという階層的なツリー構造をたどるのですが、コンストラクタだけ引数をスーパークラスの関数に引き継がれないことです。
野中さんの記事でそういうのを見た気がしたので探してみたらありました。
Flashテクニカルノート:ActionScript 2.0と1.0の継承について
[*6] クラスにコンストラクタが定義されていない場合、空の関数がコンパイル時に自動的に作成されます(「クラスの使用 - シンプルな例」参照)。また、サブクラスのコンストラクタ関数でsuper演算子による呼出しを行っていない場合、コンパイラは自動的にスーパークラスのコンストラクタへの呼出しを生成します(「クラスの作成と使用」参照)。
つまりコンストラクタに関してはチェーンをたどるのではなく、空の関数を生成するんですね。
- new B("hoge")する
- Bクラスのコンストラクタがない
- Bクラスのコンストラクタがないので空のコンストラクタを生成する
- Bクラスのコンストラクタ内でスーパークラスのコンストラクタを呼び出してない(ここで引数が渡されなくなる)
- 内部的にAクラスのコンストラクタの呼び出しが生成される
- 生成されたコンストラクタが呼ばれる
この状態を模式的にあらわすと下記のような仮のコンストラクタを生成しているような感じだとおもいます。
//生成された状態(引数がなくなってる><) function B(){ super(); }
プロパティやメソッドにおいては発生しないところなので混乱してしまいましたがこれでスッキリしました。
わからないときこそ説明する気持ちで調べて書いてみるとよくわかっていいですね
あと、実はstaticメンバについてまだ理解し切れてないのですが、それはまたつぎにします。