ActionScript 3.0で始めるオブジェクト指向スクリプティング

第21回プロパティのようにアクセスするメソッド - get/setアクセサメソッド

前回の第20回はクラスMyTimerに修正を加えて、public属性のメソッドgetElapsedTime()がMyTimerInfoインスタンスを返すようにした(前回のサンプルファイルは、2ページからダウンロードできる⁠⁠。MyTimerInfoクラスには時分秒ミリ秒をそれぞれ納めるint型のインスタンスプロパティが宣言され、数値でなければ設定できない図1⁠。これらのプロパティを、もっときめ細かく管理できないだろうか。これが今回のテーマである。

図1 int型のプロパティに文字列を設定するとコンパイルエラーになる
図1 int型のプロパティに文字列を設定するとコンパイルエラーになる

プロパティを設定するメソッドの定義

たとえば、MyTimerInfoインスタンスのsecondsプロパティに、整数150を代入してみよう。データ型は正しいので、そのまま150が設定される図2⁠。しかし、時間は60秒につき1分繰上がるのだから、できればminutesプロパティに120秒分の2分が加算され、secondsプロパティは30になってほしい。

図2 60秒以上の値をプロパティsecondsに設定してもminutesに繰上がらない
図2 60秒以上の値をプロパティsecondsに設定してもminutesに繰上がらない

プロパティsecondsの値をminutesに繰上げるには、設定のためのメソッドをMyTimerInfoクラスに用意することが考えられる。secondsプロパティを設定するメソッドは、クラス MyTimerInfo前回のスクリプト2にたとえばsetSeconds()としてつぎのように定義すればよい(他のプロパティは一旦脇に置く⁠⁠。

// ActionScript 3.0クラス定義ファイル: MyTimerInfo.as
package {
  public class MyTimerInfo {
    public var milliseconds:int;
    public var seconds:int;
    public var minutes:int;
    public var hours:int;
    public function MyTimerInfo(nMilliseconds:Number=0) {
      setTime(nMilliseconds);
    }
    public function setSeconds(nSeconds:Number) {   // 追加
      setTime(nSeconds*1000);
    }
    private function setTime(nTime:Number):void {
      milliseconds = nTime % 1000;
      nTime = Math.floor(nTime / 1000);
      seconds = nTime % 60;
      nTime = Math.floor(nTime / 60);
      minutes = nTime % 60;
      hours = Math.floor(nTime / 60);
    }
  }
}

MyTimerInfoインスタンス(oElapsedTime)に対して、このsetSeconds()により設定値の秒数を渡せば、60秒ごとに1分がminutesプロパティに繰上がる。たとえば、setSeconds()メソッドの引数に150秒を渡せば、プロパティminutesに2分が、secondsに30秒が設定される図3⁠。

図3 プロパティsecondsに設定した値は60秒ごとに1分がminutesに繰上がる
図3 プロパティsecondsに設定した値は60秒ごとに1分がminutesに繰上がる

get/setアクセサメソッド

前項『プロパティを設定するメソッドの定義』に述べたとおり、メソッドを定義すればプロパティに対して単にデータ型の指定だけでなく、値の範囲を限定したり、他のプロパティの値と関連づけることができる。しかし、値を設定するという操作は、プロパティの方が端的でわかりやすい。ActionScript 3.0には、プロパティと同じアクセスの仕方で呼出せる特別なメソッドがある。それがgetおよびsetアクセサメソッドだ。

getおよびsetアクセサメソッドは、メソッドにgetおよびset定義キーワードを添えて、以下のように同じメソッド名で定義する。アクセサメソッドにはpublic属性を指定しつつ、内部的に用いるプロパティはprivateとするのが基本だ。

【get/setアクセサメソッドの定義】
private var プロパティ:データ型;
public function get アクセサメソッド():データ型 {
  return プロパティ;
}
public function set アクセサメソッド(引数:データ型):void {
  プロパティ = 引数;
}

getとsetのアクセサメソッドには、プロパティと同じようにアクセスできる。つまり、MyTimerInfoクラスに秒数のprivateプロパティを_secondsで宣言し、getおよびsetアクセサメソッドをsecondsとして以下のように定義し直す(ここでも他のプロパティは一旦脇に置く)と、秒数のプロパティがsecondsであるかのようにアクセスできる図4⁠。

// ActionScript 3.0クラス定義ファイル: MyTimerInfo.as
package {
  public class MyTimerInfo {
    public var milliseconds:int;
    private var _seconds:int;
    public var minutes:int;
    public var hours:int;
    public function MyTimerInfo(nMilliseconds:Number=0) {
      setTime(nMilliseconds);
    }
    public function get seconds():int {
      return _seconds;
    }
    public function set seconds(nSeconds:int) {
      setTime(nSeconds*1000);
    }
    private function setTime(nTime:Number):void {
      milliseconds = nTime % 1000;
      nTime = Math.floor(nTime / 1000);
      _seconds = nTime % 60;
      nTime = Math.floor(nTime / 60);
      minutes = nTime % 60;
      hours = Math.floor(nTime / 60);
    }
  }
}
図4 アクセサメソッドsecondsにはプロパティのようにアクセスできる
図4 アクセサメソッドsecondsにはプロパティのようにアクセスできる

それでは、改めてクラスMyTimerInfoにget/setアクセサメソッドを定義したい。前回のスクリプト2とは異なり、実際のインスタンスプロパティはprivate属性で指定するので、タイムラインから直接アクセスされることはない。代わりに、get/setアクセサメソッドがプロパティの役割を果たす。すると、実際のプロパティが時分秒ミリ秒の値をそれぞれもつ必要はない。

したがって、privateのインスタンスプロパティは総ミリ秒数のみを保持することとして、時分秒ミリ秒の値はアクセサメソッドの方で分ければよい[1]⁠。そこで、つぎのスクリプト1のように総ミリ秒数のprivateプロパティはtotalMillisecondsとして宣言し、hours、minutes、seconds、millisecondsはアクセサメソッドとして定義した。

スクリプト1 クラスMyTimerInfoにget/setアクセサメソッドを定義
// ActionScript 3.0クラス定義ファイル: MyTimerInfo.as
package {
  public class MyTimerInfo {
    private var totalMilliseconds:Number;
    public function MyTimerInfo(nMilliseconds:Number=0) {
      totalMilliseconds = nMilliseconds;
    }
    public function get milliseconds():int {
      var nMilliseconds:int = totalMilliseconds%1000;
      return nMilliseconds;
    }
    public function get seconds():int {
      var nSeconds:int = Math.floor(totalMilliseconds/1000)%60;
      return nSeconds;
    }
    public function get minutes():int {
      var nMinutes:int = Math.floor(totalMilliseconds/1000/60)%60;
      return nMinutes;
    }
    public function get hours():int {
      var nHours:int = Math.floor(totalMilliseconds/1000/60/60);
      return nHours;
    }
    public function set milliseconds(nMilliseconds:int):void {
      setTime(hours, minutes, seconds, nMilliseconds);
    }
    public function set seconds(nSeconds:int):void {
      setTime(hours, minutes, nSeconds, milliseconds);
    }
    public function set minutes(nMinutes:int):void {
      setTime(hours, nMinutes, seconds, milliseconds);
    }
    public function set hours(nHours:int):void {
      setTime(nHours, minutes, seconds, milliseconds);
    }
    public function setTime(nHours:int=0, nMinutes:int=0, nSeconds:int=0, nMilliseconds:int=0):void {
      nMinutes = nHours*60+nMinutes;
      nSeconds = nMinutes*60+nSeconds;
      nMilliseconds = nSeconds*1000+nMilliseconds;
      totalMilliseconds = nMilliseconds;
    }
  }
}

この仕様変更にともない、setTime()は時分秒ミリ秒をそれぞれ引数として受取るメソッドに定義し直し、総ミリ秒数のprivateプロパティtotalMillisecondsを設定することとした。各メソッドの処理内容は、前回までの説明で理解できよう。なお、setTime()メソッドはpublic属性にしたので、タイムラインから呼出して、時分秒ミリ秒をまとめて設定することも可能だ。

アクセサメソッドsecondsは、タイムラインからたとえばつぎのようなフレームアクションで試すことができる図5⁠。

// タイムライン: メイン
// フレームアクション
var myObject:MyTimer = new MyTimer();
var oElapsedTime:MyTimerInfo = myObject.getElapsedTime();
oElapsedTime.seconds = 150;   // 150秒を設定
trace(oElapsedTime.minutes, oElapsedTime.seconds);
oElapsedTime.seconds -= 50;   // 50秒を差引く
trace(oElapsedTime.minutes, oElapsedTime.seconds);
図5 アクセサメソッドでプロパティ値を変更する
図5 アクセサメソッドでプロパティ値を変更する

これでクラス定義の基本は、ひととおり解説した。次回からはお題を変えて、MovieClipシンボルにクラスを設定してみたい。

今回解説した次のサンプルファイルがダウンロードできます。

おすすめ記事

記事・ニュース一覧