Flash CS3 ドキュメンテーション |
|||
| ActionScript 3.0 のプログラミング > ActionScript のオブジェクト指向プログラミング > 継承 | |||
継承は、コード再利用の一形式です。プログラマは継承を使用して、既存のクラスに基づく新しいクラスを開発することができます。既存のクラスは "基本クラス" または "スーパークラス"と呼ばれますが、新しいクラスは、通常は "サブクラス" と呼ばれます。継承の主な利点は、既存のコードをそのまま維持しながら、基本クラスのコードを再利用できることです。さらに、継承では他のクラスが基本クラスとやり取りする方法を変更する必要がありません。入念にテストされたか、既に使用されている既存クラスを変更するのではなく、継承を使用することでそのクラスを追加プロパティまたはメソッドで拡張できる統合モジュールとして使用できます。したがって、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()); // 出力 : 3.141592653589793
var sq:Square = new Square();
trace(sq.area()); // 出力 : 1
各クラスはデータ型を定義するため、継承を使用すると、基本クラスと基本クラスを拡張するクラスの特別な関係が作成されます。サブクラスは、基本クラスのすべてのプロパティを保有します。これは、サブクラスのインスタンスは常に基本クラスのインスタンスの代わりに使用できることを意味します。たとえば、メソッドが Shape 型のパラメータを定義する場合、Circle 型は Shape 型を拡張するので Circle 型のパラメータを渡すことができます。次に例を示します。
function draw(shapeToDraw:Shape) {}
var myCircle:Circle = new Circle();
draw(myCircle);
インスタンスプロパティは、function、var、または const キーワードのいずれで定義されているかにかかわらず、プロパティが基本クラスの private 属性で宣言されていない限り、すべてのサブクラスに継承されます。たとえば、Flash Player API の 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 キーワードは、private、protected、および 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 として宣言され、この結果、次の抜粋に示すようにコードがコンパイルされて実行されます。
// "foo" フォルダ内の "Base.as"
package foo
{
public class Base
{
public var str:String = "hello"; // この行の public を変更
}
}
// "bar" というフォルダ内の Extender.as
package bar
{
import foo.Base;
public class Extender extends Base
{
public function getString():String {
return str;
}
}
}
// "ProtectedExample.as" というファイル内のメインアプリケーションクラス
import flash.display.MovieClip;
import bar.Extender;
public class AccessControl extends MovieClip
{
public function AccessControl()
{
var myExt:Extender = new Extender();
trace(myExt.testString); // str が public でない場合はエラー
trace(myExt.getString()); // str が private または internal の場合はエラー
}
}
}
その他のアクセス制御指定子が前の例のコンパイルおよび実行に与える影響を確認するには、AccessControl クラスから次の行を削除またはコメントアウトした後、str 変数のアクセス制御指定子を private、protected、または internal に変更します。
trace(myExt.testString); // str が public でない場合はエラー
var または const キーワードで宣言されたプロパティは継承されますが、オーバーライドすることはできません。プロパティをオーバーライドすると、サブクラスでプロパティが再定義されます。オーバーライドできるプロパティの型はメソッド、すなわち function キーワードで宣言されるプロパティのみです。インスタンス変数をオーバーライドすることはできませんが、インスタンス変数の getter および setter メソッドを作成し、これらのメソッドをオーバーライドすることで、同じ機能を実現できます。詳細については、getter および setter のオーバーライドを参照してください。
メソッドをオーバーライドすると、継承されたメソッドの動作が再定義されます。静的メソッドは継承されず、オーバーライドすることはできません。ただし、次の 2 つの条件が満たされていれば、インスタンスメソッドはサブクラスに継承され、オーバーライドできます。
final キーワードで宣言されていない。インスタンスメソッドで使用された場合、final キーワードは、サブクラスによってメソッドがオーバーライドされないように、プログラマが意図的に指定したことを示します。private アクセス制御指定子で宣言されていない。基本クラス内でメソッドが private としてマークされている場合、基本クラスのメソッドはサブクラスから参照できないので、サブクラス内で同じ名前のメソッドを定義するときに override キーワードを使用する必要はありません。上記の条件を満たすインスタンスメソッドをオーバーライドするには、サブクラス内のメソッドの定義は override キーワードを使用し、次の方法でメソッドのスーパークラスバージョンと一致する必要があります。
ただし、オーバーライドメソッドのパラメータの名前は、パラメータの数および各パラメータのデータ型が一致していれば、基本クラスのパラメータの名前と一致する必要はありません。
メソッドをオーバーライドするとき、多くのプログラマが、動作を完全に置き換えるのではなく、オーバーライドするスーパークラスメソッドの動作に追加したいと考えるでしょう。これには、サブクラス内のメソッドがそれ自体のスーパークラスバージョンを呼び出すメカニズムが必要です。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()); // 出力 : 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 をオーバーライドすることができます。たとえば、次のコードでは、Flash Player API の 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); // エラー
}
}
}
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); // 出力 : 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); // 出力 : 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); // 出力 : instance
}
}
このページに新しいコメントが追加された場合に、電子メールでの通知を希望する。 | コメントレポート
現在のページ: http://livedocs.adobe.com/flash/9.0_jp/main/00000068.html