サイトトップ

Director Flash 書籍 業務内容 プロフィール

Macromedia Flash非公式テクニカルノート

Eventのサブクラスでclone()とtoString()メソッドをオーバーライドする

ID: FN1107003 Product: Flash CS3 and above Platform: All Version: 9 and above/ActionScript 3.0

DisplayObjectはEventDispatcherクラスを継承します。したがって、タイムラインに表示するクラスは、イベントリスナーの登録・削除を始め、イベントの配信もできます。そして、独自のイベントをカスタムイベントクラスで定めれば、リスナーに渡す情報が自由に選べます。

さて、[ヘルプ]の[ActionScript 3.0開発ガイド]/[コアActionScriptクラス]/[イベント処理]/[イベントオブジェクト]で「Eventクラスのサブクラス」の項を見るとつぎのように説明されています。

Eventサブクラスを作成する場合は、clone()とtoString()メソッドをオーバーライドして、サブクラスに固有の機能を提供する必要があります。

ところが、多くの場合これらのメソッドを再定義(オーバーライド)しなくても、とくに不都合は生じません。本ノートでは、このふたつのメソッドをなぜ定めなければならないのか、また定めないと何が起こるのかをご説明します。


01 Event.clone()メソッドがいつ必要か
まずは、Event.clone()メソッドです。[ヘルプ]の説明には、「Eventサブクラスのインスタンスを複製」するメソッドだとされています。ただし、このメソッドは、通常とくに呼出す必要はありません。一度配信したイベントオブジェクトを再び配信すると、内部的に呼出されるメソッドだからです。

そこで、テスト用にEventのサブクラス(MyEvent)を定義してみましょう(スクリプト001)。スーパークラスEventのコンストラクタメソッドには、必ずイベント名の文字列を渡さなければなりません(第7行目)。スーパークラスのコンストラクタメソッドはsuperステートメントで呼出します。イベント名の文字列は静的定数(TEST)として定めました(第4行目)。また、このクラス独自のプロパティ(date)をひとつ宣言し(第5行目)、コンストラクタで初期値を与えています(第8行目)。

スクリプト001■Eventを継承したカスタムイベントクラスMyEventの定義
    // ActionScript 3.0クラス定義ファイル: MyEvent.as
  1. package {
  2.   import flash.events.Event;
  3.   public class MyEvent extends Event {
  4.     public static const TEST:String = "test";
  5.     public var date:Date;
  6.     public function MyEvent() {
  7.       super(TEST);
  8.       date = new Date();
  9.     }
  10.   }
  11. }

このカスタムイベントMyEventのインスタンスを、同じ場所に保存したムービー(FLA)ファイルのフレームアクションで配信します(スクリプト002)。リスナー関数(traceEvent)も同じフレームアクションに定め(第9〜11行目)、カスタムイベント定数(MyEvent.TEST)で登録しました(第2行目)。イベントはEventDispatcher.dispatchEvent()メソッドにイベントオブジェクトを渡して配信します(第6〜7行目)。

スクリプト002■カスタムイベントをリスナーに配信する
    // フレームアクション
  1. // var eventObject:MyEvent = new MyEvent();
  2. addEventListener(MyEvent.TEST, traceEvent);
    // イベントを2回配信
  3. testEvent();
  4. testEvent();
  5. function testEvent() {
  6.   var eventObject:MyEvent = new MyEvent();   // ローカル変数にイベントオブジェクト
  7.   dispatchEvent(eventObject);
  8. }
  9. function traceEvent(eventObject:MyEvent):void {
  10.   trace(eventObject);   // 確認用
  11. }

スクリプト002でイベントを配信するテスト用の関数(testEvent())は2度呼出しています。したがって、リスナー関数(traceEvent())は確認のためのイベントオブジェクトを2回[出力]します(図001)。エラーなどの問題はとくに生じません。

図001■イベントを配信する関数は2度呼出す

スクリプト002を少し書替えます(スクリプト003)。イベントオブジェクトを関数内のローカル変数に入れず、タイムライン変数に予め納めておきます(第6行目を第1行目で替えます)。すると、テスト用の関数(testEvent())は呼ばれるたびにイベントオブジェクトをつくるのでなく、毎回同じオブジェクトを配信することになります。

スクリプト003■タイムライン変数のイベントオブジェクトを使い回して配信する
    // フレームアクション
  1. var eventObject:MyEvent = new MyEvent();   // タイムライン変数にイベントオブジェクト
  2. addEventListener(MyEvent.TEST, traceEvent);
    // イベントを2回配信
  3. testEvent();
  4. testEvent();
  5. function testEvent() {
  6.   // var eventObject:MyEvent = new MyEvent();
  7.   dispatchEvent(eventObject);
  8. }
  9. function traceEvent(eventObject:MyEvent):void {
  10.   trace(eventObject);
  11. }

[ムービープレビュー]を確かめると、「強制型変換に失敗しました」というランタイムエラー#1034が示されます(図002)。もっともよく見ると、1行目の[出力]は前掲スクリプト002と同じです(前掲図001下図参照)。つまり、エラーを起こしたのは、イベントの2回目の配信(スクリプト003第4行目)なのです。

図002■強制型変換に失敗したというランタイムエラーが示される

新たにつくったイベントオブジェクトは、1度しか配信できません。すでに送ったイベントオブジェクトをEventDispatcher.dispatchEvent()メソッドに渡すと、内部的にEvent.clone()メソッドが呼出され、戻り値の複製されたイベントオブジェクトを配信します。

ところが、継承したEvent.clone()メソッドでは、サブクラスのオブジェクトは複製できません。複製されたスーパークラスEventのオブジェクトは、サブクラスのインスタンスとしては扱えないというのがランタイムエラーの意味することです(前掲図002下図)。


02 Event.clone()メソッドをオーバーライドする
そこで、サブクラスのインスタンスを複製するように、サブクラス独自のclone()メソッドを定めなければなりません。スーパークラスに備わっているメソッドをサブクラスで定義し直すことは「オーバーライド」と呼ばれます。オーバーライドするメソッドにはoverride属性キーワードを添え、メソッド名のほかアクセス制御の属性ならびに引数とそのデータ型および戻り値のデータ型をすべてスーパークラスと同じにしなければなりません。

カスタムイベントクラス(MyEvent)にclone()メソッドを再定義したのが以下のスクリプト004です(第10〜15行目)。コンストラクタメソッドで新規のオブジェクトをつくったうえで、ただひとつの独自プロパティ(date)をコピーして返しています。なお、呼出されたことが確かめられるようにtrace()関数を加えておきました。

スクリプト004■clone()メソッドをオーバーライドしたカスタムイベントクラスMyEvent
    // ActionScript 3.0クラス定義ファイル: MyEvent.as
  1. package {
  2.   import flash.events.Event;
  3.   public class MyEvent extends Event {
  4.     public static const TEST:String = "test";
  5.     public var date:Date;
  6.     public function MyEvent() {
  7.       super(TEST);
  8.       date = new Date();
  9.     }
  10.     public override function clone():Event {
  11.       var eventObject:MyEvent = new MyEvent();
  12.       eventObject.date = date;
  13.       trace(eventObject.date);   // 確認用
  14.       return eventObject;
  15.     }
  16.   }
  17. }

前掲スクリプト003のフレームアクションはそのままで[ムービープレビュー]を試すと、エラーは出なくなります。そして、[出力]パネルの2行目にDateオブジェクト()の文字列が表れるのは、オーバーライドしたclone()メソッドのtrace()結果で、2回目の配信でこのメソッドが呼ばれたことを示します(図003)。

図003■イベントオブジェクトの2回目の配信でclone()メソッドが呼出される

これでイベントオブジェクトを使い回しても、2度目以降は再定義したclone()メソッドがオブジェクトを複製して、エラーなく配信が行われます。


03 Event.toString()メソッドをオーバーライドする
Event.toString()メソッドは、オーバーライドしないからといってことさら問題は起こりません。このメソッドは、インスタンスの文字列表現(String型)を返します。インスタンスの文字列表現を確かめたいおもな場合のひとつが、trace()関数による[出力]です。たとえば、KeyboardEventやMouseEventクラスのイベントオブジェクトを[出力]すると、つぎのようにクラスに応じたオブジェクトの情報が得られます。

[KeyboardEvent type="keyDown" bubbles=true cancelable=false eventPhase=2 charCode=0 keyCode=0 keyLocation=0 ctrlKey=false altKey=false shiftKey=false]

[MouseEvent type="click" bubbles=true cancelable=false eventPhase=2 localX=0 localY=0 stageX=0 stageY=0 relatedObject=null ctrlKey=false altKey=false shiftKey=false buttonDown=false delta=0]

Event.toString()メソッドが返す文字列の構成は、つぎのようにクラス名とイベント名から始まり、オブジェクトのプロパティとその値が続きます[*1]

[イベントクラス名 type=イベント名 プロパティ=値 ...]

ところが、前掲スクリプト004のクラスMyEventは、Event.toString()メソッドをオーバーライドしていません。そのため、スーパークラスのEvent.toString()メソッドが呼出されて、イベント名("test")を除いて通常(Event.ENTER_FRAMEなど)のEventインスタンスとまったく同じ内容になっています。

[Event type="test" bubbles=false cancelable=false eventPhase=2]

文字列表現のクラス名を正しく直し、クラス独自のプロパティ(date)も情報に加えたいところです。カスタムクラス(MyEvent)にtoString()メソッドを再定義しましょう。

このときぜひ覚えておきたいメソッドがEvent.formatToString()です。toString()メソッドが返す文字列をつくるのに、Stringクラスのメソッドでプロパティ名や値をひとつひとつつなぎ合わせる必要はありません。Event.formatToString()メソッドの引数に、戻り値として加えたいプロパティ名の文字列をただ並べて渡せばよいのです。

Eventオブジェクト.formatToString(クラス名, プロパティ名, ..., プロパティ名)

カスタムイベントクラス(MyEvent)にtoStringメソッドを再定義したのが、つぎのスクリプト005です[*2]

スクリプト005■toString()メソッドをオーバーライドしたカスタムイベントクラスMyEvent
    // ActionScript 3.0クラス定義ファイル: MyEvent.as
  1. package {
  2.   import flash.events.Event;
  3.   public class MyEvent extends Event {
  4.     public static const TEST:String = "test";
  5.     public var date:Date;
  6.     public function MyEvent() {
  7.       super(TEST);
  8.       date = new Date();
  9.     }
  10.     public override function clone():Event {
  11.       var eventObject:MyEvent = new MyEvent();
  12.       eventObject.date = date;
  13.       trace(eventObject.date);   // 確認用
  14.       return eventObject;
  15.     }
  16.     public override function toString():String {
  17.       var return_str:String =
          formatToString("MyEvent", "type", "bubbles", "cancelable", "eventPhase", "date");
  18.       return return_str;
  19.     }
  20.   }
  21. }

前掲スクリプト003のフレームアクションで[ムービープレビュー]を確かめると、クラス名はカスタムクラスMyEventに変わり、独自のプロパティdateが[出力]に加わっています(図004)。なお、toString()メソッドの戻り値は前述Event.formatToString()でつくりました(スクリプト005第17行目)。

図004■オーバーライドしたtoString()メソッドの戻り値はEvent.formatToString()メソッドで生成

[*1] イベント名の文字列を示すEvent.typeもEventクラスのプロパティのひとつです。

[*2] イベントクラスには読取り専用のプロパティが少なくありません。他方、他のクラスやフレームアクションからのアクセスを許すため、public属性で定めることになります。したがって、get/setアクセサメソッドとするのが確実でしょう。


作成者: 野中文雄
作成日: 2011年7月12日


Copyright © 2001-2011 Fumio Nonaka.  All rights reserved.