継承

継承は、コード再利用の一形式です。プログラマーは継承を使用して、既存のクラスに基づく新しいクラスを開発することができます。 既存のクラスは、基本クラスまたはスーパークラスと呼ばれますが、新しいクラスは、サブクラスと呼ばれます。継承の主な利点は、既存のコードをそのまま維持しながら、基本クラスのコードを再利用できることです。 さらに、継承では他のクラスが基本クラスとやり取りする方法を変更する必要がありません。 入念にテストされたか、既に使用されている既存クラスを変更するのではなく、継承を使用することでそのクラスを追加プロパティまたはメソッドで拡張できる統合モジュールとして使用できます。 したがって、extends キーワードを使用して、クラスが別のクラスを継承することを示すことができます。

継承を使用すると、コード内でポリモーフィズムを利用することもできます。ポリモーフィズムを使用すると、異なるデータ型に適用された場合に異なる動作を実行するメソッドに単一のメソッド名を使用できます。 単純な例として、2 つのサブクラス Circle と Square を持つ Shape という名前の基本クラスがあります。 Shape クラスは、形状の面積を返す area() というメソッドを定義します。ポリモーフィズムが実装されている場合、Circle 型および Square 型のオブジェクトで area() メソッドを呼び出して、正しい計算を行うことができます。継承では、サブクラスが基本クラスからメソッドを継承し、再定義できるようにする、つまりオーバーライドを許可することで、ポリモーフィズムが有効になります。次の例では、area() メソッドを、Circle クラスと Square クラスによって再定義します。

class Shape 
{ 
    public function area():Number 
    { 
        return NaN; 
    } 
} 
 
class Circle extends Shape 
{ 
    private var radius:Number = 1; 
    override public function area():Number 
    { 
        return (Math.PI * (radius * radius)); 
    } 
} 
 
class Square extends Shape 
{ 
    private var side:Number = 1; 
    override public function area():Number 
    { 
        return (side * side); 
    } 
} 
 
var cir:Circle = new Circle(); 
trace(cir.area()); // output: 3.141592653589793 
var sq:Square = new Square(); 
trace(sq.area()); // output: 1

各クラスはデータ型を定義するため、継承を使用すると、基本クラスと基本クラスを拡張するクラスの特別な関係が作成されます。 サブクラスは、基本クラスのすべてのプロパティを保有します。これは、サブクラスのインスタンスは常に基本クラスのインスタンスの代わりに使用できることを意味します。 例えば、メソッドが Shape 型のパラメーターを定義する場合、Circle 型は Shape 型を拡張するので Circle 型のパラメーターを渡すことができます。次に例を示します。

function draw(shapeToDraw:Shape) {} 
 
var myCircle:Circle = new Circle(); 
draw(myCircle);

インスタンスプロパティと継承

インスタンスプロパティは、functionvar、または const キーワードのいずれで定義されているかにかかわらず、プロパティが基本クラスの private 属性で宣言されていない限り、すべてのサブクラスに継承されます。例えば、ActionScript 3.0 の Event クラスには、すべてのイベントオブジェクトに共通するプロパティを継承する多数のサブクラスがあります。

一部の型のイベントでは、Event クラスにイベントを定義するために必要なすべてのプロパティが含まれます。 これらの型のイベントでは、Event クラスで定義されたもの以外のインスタンスプロパティは必要ありません。 このようなイベントの例として、データが正常にロードされたときに発生する complete イベント、ネットワーク接続が確立されたときに発生する connect イベントなどがあります。

次の例は、サブクラスに継承されるプロパティおよびメソッドの一部を示す Event クラスからの抜粋です。 プロパティが継承されるので、あらゆるサブクラスのインスタンスは、これらのプロパティにアクセスできます。

public class Event 
{ 
    public function get type():String; 
    public function get bubbles():Boolean; 
    ... 
 
    public function stopPropagation():void {} 
    public function stopImmediatePropagation():void {} 
    public function preventDefault():void {} 
    public function isDefaultPrevented():Boolean {} 
    ... 
}

その他の型のイベントは、Event クラスで使用できない固有のプロパティを必要とします。 これらのイベントは、Event クラスで定義されたプロパティに新しいプロパティを追加できるように、Event クラスのサブクラスを使用して定義されます。 このようなサブクラスには、mouseMove イベント、click イベントなど、マウス操作またはマウスクリックに関連付けられているイベントに固有のプロパティを追加する MouseEvent クラスなどがあります。次の例は、MouseEvent クラスからの抜粋であり、サブクラスには存在するが基本クラスには存在しないプロパティの定義を示しています。

public class MouseEvent extends Event 
{ 
    public static const CLICK:String= "click"; 
    public static const MOUSE_MOVE:String = "mouseMove"; 
    ... 
 
    public function get stageX():Number {} 
    public function get stageY():Number {} 
    ... 
}

アクセス制御指定子と継承

プロパティが public キーワードで宣言された場合、このプロパティは任意の場所のコードから参照できます。つまり、public キーワードは、privateprotected、および internal キーワードとは異なり、プロパティの継承に制限を加えません。

プロパティが private キーワードで宣言された場合、このプロパティは定義されたクラス内でのみ参照でき、サブクラスに継承されません。この動作は、旧バージョンの ActionScript とは異なります。旧バージョンでは、private キーワードは ActionScript 3.0 の protected キーワードと同じように動作しました。

protected キーワードは、定義されたクラス内だけでなく、すべてのサブクラスからもプロパティを参照できることを示します。Java の protected キーワードとは異なり、ActionScript 3.0 の protected キーワードでは、同じパッケージ内の他のすべてのクラスからプロパティを参照できるわけではありません。ActionScript 3.0 では、サブクラスのみが protected キーワードで宣言されたプロパティにアクセスできます。また、protected 指定されたプロパティは、サブクラスが基本クラスと同じパッケージ内にあるか、または異なるパッケージ内にあるかにかかわらず、サブクラスから参照できます。

プロパティが定義されているパッケージからのプロパティへの参照を制限するには、internal キーワードを使用するか、またはアクセス制御指定子を一切使用しないようにします。internal アクセス制御指定子は、アクセス制御指定子が指定されていないときに適用されるデフォルトのアクセス制御指定子です。internal とマークされたプロパティは、同じパッケージ内に存在するサブクラスによってのみ継承されます。

次の例を使用して、各アクセス制御指定子がパッケージ境界を越えてどのように継承に影響するかを確認できます。 次のコードは、AccessControl というメインアプリケーションクラスと Base および Extender というその他のクラス 2 つを定義します。 Base クラスは foo パッケージ内にあり、Base クラスのサブクラスである Extender クラスは bar パッケージ内にあります。 AccessControl クラスは Extender クラスのみを読み込み、Base クラスで定義されている str という変数にアクセスしようとする Extender のインスタンスを作成します。str 変数は public として宣言され、この結果、次の抜粋に示すようにコードがコンパイルされて実行されます。

// Base.as in a folder named foo 
package foo 
{ 
    public class Base 
    { 
        public var str:String = "hello"; // change public on this line 
    } 
} 
 
// Extender.as in a folder named bar 
package bar 
{ 
    import foo.Base; 
    public class Extender extends Base 
    { 
        public function getString():String { 
            return str; 
        } 
    } 
} 
 
// main application class in file named AccessControl.as 
package 
{ 
    import flash.display.MovieClip; 
    import bar.Extender; 
    public class AccessControl extends MovieClip 
    { 
        public function AccessControl() 
        { 
            var myExt:Extender = new Extender(); 
            trace(myExt.str);// error if str is not public 
            trace(myExt.getString()); // error if str is private or internal 
        } 
    } 
}

その他のアクセス制御指定子が前の例のコンパイルおよび実行に与える影響を確認するには、AccessControl クラスから次の行を削除またはコメントアウトした後、str 変数のアクセス制御指定子を privateprotected、または internal に変更します。

trace(myExt.str);// error if str is not public

変数のオーバーライド禁止

var または const キーワードで宣言されたプロパティは継承されますが、オーバーライドすることはできません。プロパティをオーバーライドすると、サブクラスでプロパティが再定義されます。 オーバーライドできるプロパティの型は、get アクセサーと set アクセサー(function キーワードで宣言されるプロパティ)のみです。インスタンス変数をオーバーライドすることはできませんが、インスタンス変数の getter および setter メソッドを作成し、これらのメソッドをオーバーライドすることで、同じ機能を実現できます。

メソッドのオーバーライド

メソッドをオーバーライドすると、継承されたメソッドの動作が再定義されます。 静的メソッドは継承されず、オーバーライドすることはできません。 ただし、次の 2 つの条件が満たされていれば、インスタンスメソッドはサブクラスに継承され、オーバーライドできます。

  • インスタンスメソッドが基本クラス内で final キーワードで宣言されていません。インスタンスメソッドで使用された場合、final キーワードは、サブクラスによってメソッドがオーバーライドされないように、プログラマーが意図的に指定したことを示します。

  • インスタンスメソッドが基本クラス内で private アクセス制御指定子で宣言されていません。基本クラス内でメソッドが private としてマークされていると、基本クラスのメソッドはサブクラスから参照できないので、サブクラス内で同じ名前のメソッドを定義するときに override キーワードを使用する必要はありません。

    上記の条件を満たすインスタンスメソッドをオーバーライドするには、サブクラス内のメソッドの定義は override キーワードを使用し、次の方法でメソッドのスーパークラスバージョンと一致する必要があります。

  • オーバーライドメソッドには、基本クラスのメソッドと同じレベルのアクセス制御が必要です。 internal とマークされたメソッドには、アクセス制御指定子を持たないメソッドと同じレベルのアクセス制御が必要です。

  • オーバーライドメソッドには、基本クラスのメソッドと同じ数のパラメーターが必要です。

  • オーバーライドメソッドのパラメーターには、基本クラスのメソッドのパラメーターと同じデータ型注釈が必要です。

  • オーバーライドメソッドには、基本クラスのメソッドと同じ戻り値の型が必要です。

ただし、オーバーライドメソッドのパラメーターの名前は、パラメーターの数および各パラメーターのデータ型が一致していれば、基本クラスのパラメーターの名前と一致する必要はありません。

super ステートメント

メソッドをオーバーライドするとき、多くのプログラマーが、動作を完全に置き換えるのではなく、オーバーライドするスーパークラスメソッドの動作に追加したいと考えるでしょう。 これには、サブクラス内のメソッドがそれ自体のスーパークラスバージョンを呼び出すメカニズムが必要です。 super ステートメントは、直接のスーパークラスへの参照が含まれるそのようなメカニズムを提供します。次の例では、thanks() というメソッドを含む Base クラスと、thanks() メソッドをオーバーライドする Extender という Base クラスのサブクラスを定義します。Extender.thanks() メソッドは、super ステートメントを使用して Base.thanks() を呼び出します。

package { 
    import flash.display.MovieClip; 
    public class SuperExample extends MovieClip 
    { 
        public function SuperExample() 
        { 
            var myExt:Extender = new Extender() 
            trace(myExt.thanks()); // output: Mahalo nui loa 
        } 
    } 
} 
 
class Base { 
    public function thanks():String 
    { 
        return "Mahalo"; 
    } 
} 
 
class Extender extends Base 
{ 
    override public function thanks():String 
    { 
        return super.thanks() + " nui loa"; 
    } 
}

getter と setter のオーバーライド

スーパークラスで定義された変数をオーバーライドすることはできませんが、getter と setter をオーバーライドすることができます。 例えば、次のコードでは、ActionScript 3.0 の MovieClip クラスで定義された currentLabel という名前の getter をオーバーライドします。

package 
{ 
    import flash.display.MovieClip; 
    public class OverrideExample extends MovieClip 
    { 
        public function OverrideExample() 
        { 
            trace(currentLabel) 
        } 
        override public function get currentLabel():String 
        { 
            var str:String = "Override: "; 
            str += super.currentLabel; 
            return str; 
        } 
    } 
}

OverrideExample クラスコンストラクターの trace() ステートメントの出力は、Override: null です。これは、継承された currentLabel プロパティをオーバーライドできたことを示します。

継承されない静的プロパティ

静的プロパティはサブクラスに継承されません。 つまり、サブクラスのインスタンスから静的プロパティにアクセスすることはできません。 静的プロパティは、そのプロパティが定義されたクラスオブジェクトからのみアクセス可能です。 例えば、次のコードは、Base という基本クラスと、Extender という Base クラスを拡張するサブクラスを定義します。 静的変数 test は Base クラスで定義されています。次の抜粋で記述されているコードは、strict モードではコンパイルされず、standard モードではランタイムエラーが生成されます。

package { 
    import flash.display.MovieClip; 
    public class StaticExample extends MovieClip 
    { 
        public function StaticExample() 
        { 
            var myExt:Extender = new Extender(); 
            trace(myExt.test);// error 
        } 
    } 
} 
 
class Base { 
    public static var test:String = "static"; 
} 
 
class Extender extends Base { }

次のコードに示すように、静的変数 test には、クラスオブジェクトからアクセスする必要があります。

Base.test;

ただし、静的プロパティと同じ名前を使用してインスタンスプロパティを定義することができます。 このインスタンスプロパティは、静的プロパティと同じクラス内またはサブクラス内で定義できます。 例えば、前述の例の Base クラスでは、test というインスタンスプロパティを定義できます。次のコードはコンパイルおよび実行されます。これは、インスタンスプロパティが Extender クラスに継承されるからです。 test インスタンス変数の定義が Extender クラスにコピーされるのではなく、移動された場合も、コードはコンパイルされ、実行されます。

package 
{ 
    import flash.display.MovieClip; 
    public class StaticExample extends MovieClip 
    { 
        public function StaticExample() 
        { 
            var myExt:Extender = new Extender(); 
            trace(myExt.test);// output: instance 
        } 
    } 
} 
 
class Base 
{ 
    public static var test:String = "static"; 
    public var test:String = "instance"; 
} 
 
class Extender extends Base {}

静的プロパティとスコープチェーン

静的プロパティは継承されませんが、そのプロパティが定義されたクラスおよびそのクラスのサブクラスのスコープチェーン内にあります。 このため、静的プロパティは、定義されたクラスおよびサブクラスのスコープ内にあると言います。つまり、静的プロパティが定義されたクラスおよびそのサブクラスの本体内から直接静的プロパティにアクセスできます。

次の例では、前述の例で定義したクラスを変更して、Base クラスで定義された静的変数 test が Extender クラスのスコープ内にあることを示します。つまり、Extender クラスは、test を定義したクラスの名前を接頭辞として変数に付けなくても、静的変数 test にアクセスできます。

package 
{ 
    import flash.display.MovieClip; 
    public class StaticExample extends MovieClip 
    { 
        public function StaticExample() 
        { 
            var myExt:Extender = new Extender(); 
        } 
    } 
} 
 
class Base { 
    public static var test:String = "static"; 
} 
 
class Extender extends Base 
{ 
    public function Extender() 
    { 
        trace(test); // output: static 
    } 
     
}

インスタンスプロパティが、同じクラスまたはスーパークラス内で静的プロパティと同じ名前を使用するように定義されている場合は、インスタンスプロパティはスコープチェーン内で優先順位が高くなります。 このような場合、インスタンスプロパティは、静的プロパティをシャドウすると言います。つまり、静的プロパティの値ではなくインスタンスプロパティの値が使用されます。例えば、次のコードは、Extender クラスが test という名前のインスタンス変数を定義するときに、trace() ステートメントが静的変数の値ではなく、インスタンス変数の値を使用することを示しています。

package 
{ 
    import flash.display.MovieClip; 
    public class StaticExample extends MovieClip 
    { 
        public function StaticExample() 
        { 
            var myExt:Extender = new Extender(); 
        } 
    } 
} 
 
class Base 
{ 
    public static var test:String = "static"; 
} 
 
class Extender extends Base 
{ 
    public var test:String = "instance"; 
    public function Extender() 
    { 
        trace(test); // output: instance 
    } 
     
}