チュートリアル 05 Plus2 - Atom メソッドとインスタンス化の際に用いるアーギュメント

 

Plus1 は Max に組み込まれているPlus(+) オブジェクトの基本的な動作を行ないますが、いくつか異なる点があります。私たちの作成したクラスで、組み込みの Plus オブジェクトの機能を完全に再現するためには、インスタンス化の際に使用されるアーギュメントを取得し、addend にその値を設定する機能を持たせなければなりません。さらに、このクラスでは整数と浮動小数点数の両方を処理しなければなりません。インスタンス化アーギュメント(インスタンス化の際に使用されるアーギュメント)が浮動小数点数であった場合、オブジェクトは浮動小数点数のモードで処理を行なう必要があります。また、アーギュメントが整数の場合には、整数モードで処理を行い、浮動小数点数が入力された場合には、小数点以下の値を切り捨てて整数化する必要があります。この機能についてよくわからないときは、下のコードを読む前に + オブジェクトのヘルプファイルを開いてしばらく試してみて下さい。

import com.cycling74.max.*;

public class Plus2 extends MaxObject {

	private Atom addend = Atom.newAtom(0);
	
	public Plus2(Atom[] args) {
		declareIO(2,1);
		if (args.length > 0) {
			if (args[0].isInt() || args[0].isFloat()) {
				addend = args[0];
			}
		} 
	}
	
	public void inlet(int i) {
		if (getInlet() == 0) {
			if (addend.isInt()) {
				outlet(0, i + addend.getInt());
			} else {
				outlet(0, (float)i + addend.getFloat());
			}
		} else {
			if (addend.isInt()) {
				addend = Atom.newAtom(i);
			} else {
				addend = Atom.newAtom((float)i);
			}
		}
	}
	
	public void inlet(float f) {
		if (getInlet() == 0) {
			if (addend.isInt()) {
				outlet(0, (int)f + addend.getInt());
			} else {
				outlet(0, f + addend.getFloat());
			}
		} else {
			if (addend.isInt()) {
				addend = Atom.newAtom((int)f);
			} else {
				addend = Atom.newAtom(f);
			}
		}
	}
}

まず最初に気がつく変更点は、変数 addend の型が int から Atom に変更されていることです。Atomは 整数と浮動小数点数の両方を表現することができ、複数のモードによる処理が必要なこのオブジェクトにとって便利なものであるため、これを使用しています。、Atom クラスでは、API によって提供されている Atom.newAtom というファクトリ(factory)メソッドのうちの1つを呼び出す必要があり、この点が直接コンストラクタメソッドを呼び出してインスタンスを生成することができるクラスとは異なります。このAtom.newAtom(int) メソッドは、addend を初期化し、デフォルトで整数 0 を表すように設定するために用います。

次に気がつく変更点は、コンストラクタメソッドが、アーギュメントとして Atom の配列を取るということです。Max のオブジェクトボックスに、クラス名に続けて任意のアーギュメントを追加すると、アーギュメントは Atom の配列としてコンストラクタに渡されます。そのため、ユーザがMax 環境で新しいオブジェクトボックスを作り、"mxj Plus2 74" と入力すると、配列 args には、74 という値を持つ整数のAtom が1つ含まれます。

インレットとアウトレットの宣言に使用されるメソッドが異なる点に注意して下さい。declareIO(int, int) は2つの整数を取り、 その値に等しいインレットとアウトレットを作ります。型はすべてDataTypes.ALL. になります。これらのインレットとアウトレットの宣言が終わった後、Plus2 クラスのコンストラクタメソッドは配列 args の長さが 0 より大きいかどうかをテストします。Java では、配列をオブジェクトとして扱い、配列の length フィールドにアクセスすることによって配列の長さを判定できます。上の例では、これを行なっています。インスタンス化アーギュメントが与えられない場合、配列 args の長さは 0になり、プログラムの流れはコンストラクタメソッドを抜けます。このとき addend はデフォルトの整数 0 のままになります。

しかし、少なくとも1つの Atom が配列に存在する場合、さらにテストが行なわれます。 he isInt() メソッドとisFloat() メソッドは、 args の最初の要素が整数を表すものか、あるいは浮動小数点数を表すものかを調べます。そして、そのどちらかにあてはまる場合には、この最初の要素を addend. に割り当てます。

このクラスでは、2つの型の数値入力を受信しなければならないため、2つの inlet メソッドが必要になります。if - else 構文は入力を受け取るインレットによって処理の流れを分岐させます。右インレットで数値を受信すると、新しい Atom が作られ、addend. に割り当てられます。左インレットで数値を受信すると、outlet メソッドは入力された値と addend を加算した結果を左アウトレットから出力します。2つのinlet メソッドはisInt を使ってaddend が整数を表しているか、浮動小数点数を表しているかを判断し、必要であれば、値の加算やaddend への格納を行なう前に、入力された値を適当な型にキャスト(型変換)します。

C プログラマは、addend. で表される値の変更を行なう際に、毎回新しい Atom が作られなければならないことを不思議に感じるかもしれません。Atom クラスは不変になっています。これは、一度インスタンスが作成されると、Atom が表す値を変更することができないということを意味するものです。クラスを不変のものとして設計する理由について学ぶのであれば、Joshua Block の優れた著書 「Effective Java」をお勧めします。

組み込みの Plus(+) オブジェクトには、Plus2 クラスでも実装されていないさらにマイナーな機能があります。Plus2 のインスタンスは、左インレットで bang を受信した場合、前の結果を再び出力しなければなりません。 また、リストを受信した場合、オブジェクトは、リストの2番目の要素が右インレットに送信され、その後、最初の要素が左インレットに送信されたとみなして、このリストに応答しなければなりません。あなたの理解度をテストするために、この、まだ実装されていない機能をサポートするようにPlus2 クラスを編集してみて下さい。