MaxのJavaによる外部オブジェクト(mxjオブジェクト)を作成する場合、以下のディレクトリにあるチュートルアルが役に立ちます。
Max6(Macintosh)
/Applications/Max 6.1/java-doc/tutorial/html/index.html
Max7(Macintosh)
/Applications/Max.app/Contents/Resources/C74/java-doc/tutorial/html/index.html
おそらくMax4.5がリリースされた際に作られたドキュメントなので、部分的に情報が古いですが十分参考になる内容なので日本語訳を試みました。
Welcome
Javaプログラミング言語での外部オブジェクトのサポートはMax4.5で新しく追加された。JavaとMaxの親和性は高まり、また私たちはMaxのための外部オブジェクトプログラミングを簡単に迅速に行えるようなApplication Programming Interface(API)の作成を試みた。
ドキュメントの最初のセッションでは、コンピュータにどのようにjavaをインストールするか、またJavaとMaxの世界を橋渡しとして機能するmxjMaxオブジェクトをどのように使うかを説明する。ドキュメントはmxjで書かれたいくつかのJavaクラスを作成することを通じてステップバイステップで指導する。これらのexampleは自分自身の独自のクラスのための出発点としても役に立てることができる。
このドキュメントは、Javaでどのようにプログラムを行うかを十分に教えるものではない。これらのチュートリアルから、理解したいことのほとんどは学ぶことができると思われるが、オブジェクト指向プログラミングのコンセプトや、Java言語の基本について、時間をとって学習することを高く推奨する。
幸運なことに、ほとんどの大学や学校でJavaは教えられており、その結果、印刷物であってもネット上であっても学習の手助けの質に不足はない。初心者にとって、Sun Microsystems(→Oracle)は多くの優れたオンラインドキュメントを提供している。Javaチュートリアルは、文法の紹介から、すべての重要なコンセプトまで徹底して記述している。(OS9だけをカバーしているので、このチュートリアルで話されているMac固有の問題については、廃止され無視されるべきであることに注意してほしい。Appleのウェブサイトでは、JavaはMacOSXのファーストクラスの市民であると公言している)
他の調べる上で価値のある資料はJavapediaである。名前が示唆しているように、Javapediaプロジェクトはjavaのすべての事柄の百科事典を作ることを目的としている。まだ立ち上げられたばかりであるが、この資料はすでに広範なリンク、Javaの技術についての情報、プラクティスやプロダクト、推奨される書籍のリストに溢れている。
最後に、あなたがコードを書いているときに、API reference for Java v1.4.2は非常に貴重なものとなるだろう。
InstallationX
良いニュース。もし、あなたがOSXで実行をしているのなら、おそらくJava Virtual Machine(JVM)の適切なバージョンがシステムにインストールされているはずである。JVMがインストールされているか確かめるために、新しいターミナルウィンドウを立ち上げて、java -version
とタイプしてみよう。そうすると、このような表示を確認できるはずである。
java version "1.4.2_03"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_03-117.1)
Java HotSpot(TM) Client VM (build 1.4.2-34, mixed mode)
もし、Javaのバージョンが異なっていても1.4のいずれかである限り問題ない。javaの1.4.xバージョンは、10.2以降のすべてのMacOSのバージョンに含まれている。Javaの1.4.2は10.3.xにのみ互換性があることに気をつけなければならない。あなたは、システムプリファレンスのソフトウェアアップデートコントロールパネルを使ってオペレーティングシステムと互換性がある、最新のバージョンのJavaをコンピュータに更新したいと思うこともあるかもしれない。
OSXでのJavaのレイアウトを学ぶために、次のいくつかのステップを踏む。ターミナル上で、以下のようにJavaの配置されている中心に移動する。
[computron:~] bbn% cd /System/Library/Frameworks/JavaVM.framework/Versions/
[computron:Frameworks/JavaVM.framework/Versions] bbn% ls -l
total 16
drwxr-xr-x 9 root wheel 306 23 Sep 23:39 ./
drwxr-xr-x 10 root wheel 340 20 Feb 20:04 ../
drwxr-xr-x 3 root wheel 102 10 Jan 01:28 1.2/
drwxr-xr-x 3 root wheel 102 10 Jan 01:28 1.3/
drwxr-xr-x 7 root wheel 238 23 Sep 23:39 1.3.1/
drwxr-xr-x 8 root wheel 272 9 Jan 22:26 1.4.2/
drwxr-xr-x 5 root wheel 170 10 Jan 01:28 A/
lrwxr-xr-x 1 root wheel 1 20 Feb 20:04 Current@ -> A
lrwxr-xr-x 1 root wheel 5 2 Feb 23:21 CurrentJDK@ -> 1.4.2
最初の4つのディレクトリはそれぞれ異なったJDK(Java Development Kit)を含んでいる。このドキュメントを書いているときの最新のJDKのバージョンは1.4.2であるので、CurrentJDKというように象徴的にリンクしてある。Mxjはこの最新のバージョンを使用している。これらを見てみよう。
[computron:Frameworks/JavaVM.framework/Versions] bbn% cd CurrentJDK
[computron:JavaVM.framework/Versions/CurrentJDK] bbn% ls -l
total 0
drwxr-xr-x 8 root wheel 272 9 Jan 22:26 ./
drwxr-xr-x 9 root wheel 306 23 Sep 23:39 ../
drwxr-xr-x 10 root wheel 340 21 Jan 11:01 Classes/
drwxr-xr-x 24 root wheel 816 2 Feb 23:25 Commands/
drwxr-xr-x 12 root wheel 408 21 Jan 11:01 Headers/
drwxr-xr-x 8 root wheel 272 9 Jan 22:26 Home/
drwxr-xr-x 34 root wheel 1156 2 Feb 23:54 Libraries/
drwxr-xr-x 8 root wheel 272 20 Feb 20:04 Resources/
Classesディレクトリは、いくつかのJARパッケージを含んでいる。Librariesディレクトリは、Javaに用いるためのいくつかのMac OSのネイティブライブラリを含んでいる。また、Headersは、いくつかのCヘッダファイルを含んでいる。Commandディレクトリは。jar, java, javadocなど、伝統的にJavaのbinディレクトリの内側となるファイルが含まれている。Homeディレクトリは、よく使うようになるだろう。binディレクトリは、Headersディレクトリへの経路を含む、Commandディレクトリの中のファイルへのシンボリックリンクを含んでいる。libディレクトリには、予想しているように、すべての設定とプロパティファイルが含まれている。
ディレクトリの階層を2階層上にあがってみよう。
[computron:JavaVM.framework/Versions/CurrentJDK] bbn% cd ../..
[computron:Library/Frameworks/JavaVM.framework] bbn% pwd
/System/Library/Frameworks/JavaVM.framework
[computron:Library/Frameworks/JavaVM.framework] bbn% ls -l
total 56
drwxr-xr-x 10 root wheel 340 20 Feb 20:04 ./
drwxr-xr-x 60 root wheel 2040 20 Feb 20:04 ../
lrwxr-xr-x 1 root wheel 27 2 Feb 23:21 Classes@ -> Versions/CurrentJDK/Classes
lrwxr-xr-x 1 root wheel 28 2 Feb 23:21 Commands@ -> Versions/CurrentJDK/Commands
lrwxr-xr-x 1 root wheel 24 20 Feb 20:04 Headers@ -> Versions/Current/Headers
lrwxr-xr-x 1 root wheel 24 2 Feb 23:21 Home@ -> Versions/CurrentJDK/Home
lrwxr-xr-x 1 root wheel 23 20 Feb 20:04 JavaVM@ -> Versions/Current/JavaVM
lrwxr-xr-x 1 root wheel 29 2 Feb 23:21 Libraries@ -> Versions/CurrentJDK/Libraries
lrwxr-xr-x 1 root wheel 26 20 Feb 20:04 Resources@ -> Versions/Current/Resources
drwxr-xr-x 9 root wheel 306 23 Sep 23:39 Versions/
JavaVM.frameworkのなかのHomeディレクトリのシンボリックリンクは、ここのHomeディレクトリであり、それ自身、CurrentJDKの中のHomeディレクトリへのシンボリックリンクでもある。Extensionsフォルダの中に移動してみよう。
[computron:/Library/Java] bbn% cd Extensions
[computron:/Library/Java/Extensions] bbn% ls -l
total 2288
drwxrwxr-x 8 root admin 272 23 Sep 20:24 ./
drwxrwxr-x 5 root admin 170 20 Feb 20:04 ../
-rwxr-xr-x 1 bbn admin 54665 28 Aug 2002 activation.jar*
-rw-r--r-- 1 bbn staff 364863 7 Jun 2001 javaSpeechFramework.jar
-rwxr-xr-x 1 bbn admin 305434 28 Aug 2002 mail.jar*
-rw-r--r-- 1 bbn admin 215441 23 May 2003 mysql-connector-java-3.0.8-stable-bin.jar
もし、あなたがJavaの開発を始めたばかりであったら、Extensionsフォルダの中はおそらく空である。Library/Java/Extensionsは、クラスファイルを、システムのすべてのユーザがアクセス可能にしたい場合の場所である。もし、あなたが共有マシンで作業している場合、グループの混乱を制限し、あなたのファイルを他人が変更するのを防ぐために、自身のホームディレクトリの中に、 ~/Library/Java/Extensionsフォルダを作ることができる。上記のリストの中に、Java開発の可能性を広げる4つのJARファイルがある。activation.jarは、JavaBeans Activation Frameworkを含んでいる。javaSpeechFramework.jarはコンピュータに、スピーチコマンドを話させ、受け取るために必要なクラスが含まれている。mail.jarはemailを送信したり、受信したりすることができるクラスが含まれている。mysql-connector-java-3.0.8-stable-bin.jarは、一般的なオープンソースデータベースソフトウェアであるMySQLのためのJDBCドライバである。
InstallationXP
残念なことに、WindowsはJavaが入った状態で出荷されておらず、最初にやらなければならないことは、J2SE(Java 2 Software Environment)のインストールである。
Java 2 Platform、Standard Edition 1.4をここからをダウンロードする。もし、あなたが他の人が開発したmxjオブジェクトを実行することだけに興味があるならば、JRE(Java Runtime Environment)をダウンロードするだけで良い。しかし、自分独自のJavaの外部オブジェクトを開発したいならば、SDK(Software Development Kit)もダウンロードする必要がある。
このドキュメントを書いている現在、もっとも最新のJ2SEのバージョンは1.4.2_03である。ダウンロードは2つの方法がある。"online installation"は最小限のファイルのダウンロードで済むが、インストールの間、インターネットに繋いでおかなければならない。一方、"offline installation"の選択は、すべてのファイルが含まれるので、ダウンロードするファイルは大きくなる。どちらの方法でも結果は一緒である。
通常のインストールの後、すべてのJavaに関連したファイルやプログラムは、Program Files/Javaのバージョンナンバーで構成されたサブフォルダに保持してある。インストールの確認のため、コマンドプロンプトを立ち上げ、java -version
とタイプしてみよう。以下のようなものを確認できるはずである。
java version "1.4.2_03"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_04-b05)
Java HotSpot(TM) Client VM (build 1.4.2_04-b05, mixed mode)
Using
Javaについていえば、classは変数(データ)とメソッド(アクション)のセットを定義したプロトタイプである。Javaオブジェクトは、classのメンバーとして作られる。あなたのMaxがインストールされた中のjavaディレクトリは、mxjオブジェクトとして使うことができるJavaクラスを保持するclassesというサブディレクトリを持っている。Max上で、新しいmxjオブジェクトを作成したとき、最初の引数は、このディレクトリの中に存在するクラスの名前でなくてはならない。mxjオブジェクトのインスタンスが正しく作成されると、mxjオブジェクトと、引数として与えられたクラスのJavaオブジェクトへのインターナルの2つのオブジェクトを生成する。これら2つのオブジェクトを"ピア"オブジェクトと呼ぶことができる。mxjオブジェクトは、単に、MaxとJavaの世界を結びつける導管として機能するシェルである。標準のMaxオブジェクトとは異なり、Javaクラスの名前は、大文字小文字の区別に敏感であることに気をつけなければならない。下の図はsbufJavaクラスのインスタンスが適切に作られたmxjオブジェクトを示す。
classesフォルダに格納されたclassファイルは、バイトコードファイルであることが知られている。プラットフォーム特有の機械語にコンパイルされるCのエキスターナルと違って、javaのバイトコードは、JVM(Java Virtual Machine)で動作するので、どのシステムでも実行される。Maxのエキスターナルと違って、mxjでコンパイルされるクラスファイルは、クロスプラットフォームであることを意味する。これらのバイトコードのコンパイルに使われる.javaファイルは、同じディレクトリに存在する。もし、あなたが開発者であるならば、これらのファイルを、独自のmxjにコンパイル可能なjavaクラスの出発点として利用し、あなたの希望に沿うような振る舞いに変更することができる。もし、開発者でないのなら、、独自のMaxオブジェクトを開発することを学ぶ時間はそんなにかからない。それは思っているよりも簡単なことである。
複数のclassをpackageの中に集めることが可能である。java/libディレクトリには、max.jarという名前のpackageを見つけることができる。このpackageはMaxとJavaとのコミュニケーションを容易にするコードを含んでおり、libディレクトリに入ったこのファイルの位置は重要である。Cycling '74はこれらのクラスのソースコードを配布していないが、API(Application Programming Interface)のspecification を利用できるようにしており、開発者はそのコードを独自のクラスで使用することができる。
java/helpディレクトリは、私たちが実装したクラスを調べるのに使うことができるパッチのセレクションを含んでいる。
Compiling
Javaクラスを開発するために、Javaコードのテキストファイルを作成・編集し、それから、Java Virtual Machine(JVM)が必要とするようなバイトコードである.classファイルにコンパイルする。このJavaコードの伝統的なコンパイル法は、コマンドラインから実行可能なコンパイラであるjavacを使うものである。OSXであればターミナルを用い、WindowsXPであればDOSコマンドプロンプト、またCygwinのようなunixエミュレーティングレイヤーで行われる。たとえば、ワーキングディレクトリが、MyClass.javaという名前のテキストファイルを含んでいた場合、コマンド
javac MyClass.java
が、テキストファイルの中のソースコードをコンパイルし、ワーキングディレクトリにMyClass.classというバイトコードファイルを生成する。もしコードが複雑な構成をとっていた場合、いくつかの異なった.classファイルを生成することも可能である。もしコードがエラーを含んでいた場合、コンパイラはエラーの詳細を出力する。IBMのjikesは他のコマンドラインJavaコンパイラで同様に使用することができる。
もし、あなたがこれを最初に読んで、java/classesの中のクラスで上記のコマンドを実行しようとしているなら、コンパイラはおそらくJavaコンパイラはmax.jarを見つけるためにどこを調べればよいかがわからないという内容に関連したエラーを返すだろう。これを直すために、classpathにmax.jarを追加しなければならない。これはいくつかの方法があるが、おそらくもっとも素直な方法は、コマンドラインのコンパイラの引数に渡すものである。Mac(Max4.5)の場合、
javac -classpath "/Applications/Max*/Cycling '74/java/lib/max.jar" MyClass.java
Windows(Max4.5)の場合、
javac -classpath "\Program Files\Common Files\Cycling '74\java\lib\max.jar" MyClass.java
チュートリアルのexampleのための.javaソースコードはjava/doc/tutorial/srcディレクトリで見つけることができるので、MacでHelloWorld1クラスをコンパイルするのに以下のコマンドを使用することができる。
javac -classpath "/Applications/Max*/Cycling '74/java/lib/max.jar" HelloWorld1.java
もし、unixのターミナルを使っているならば、javacコンパイラについて学ぶために、そのマニュアルページを呼び出すことができる。
% man javac
もし、コマンドラインがあなたのスタイルでない場合、落胆することはない。実際にはMax環境の中ですべてのJava開発を行うことが可能である。mxjオブジェクトはviewsourceメッセージを受け取った後、編集環境をローンチする。この環境については、次のquickieクラスについて話すときに説明する。
多くの利用可能なIDE(Integrated Development Environment)パッケージを使ってもJavaクラスをコンパイルすることができる。java/doc/ide(Max4.5)ディレクトリの中には、AppleのXcode、IBMのEclipseという2つの偉大なオプションのためのexampleプロジェクトを提供している。Xcodeには単一引用符のある名前のパスによって起こるバグがあるので、Xcodeプロジェクトは、Cycling '74ディレクトリの中に置かないようにしてほしい。
quickie
前の章で述べたように、"viewsource"メッセージをインスタンスに送ると、編集環境が起動される。ピアJavaクラスオブジェクトのための.javaファイルと、classファイルが同じディレクトリにあった場合、ソースコードは、エディタウィンドウに読み込まれる。このままコードの変更ができ、エディタウィンドウのFile:Saveメニューアイテムから、保存することができる。またJava:Compileメニューアイテムから、コンパイルすることもできる。コンパイルする前に、MXJ Compile Windowはソースコードの場所、コンパイラコマンド、ビルドディレクトリ、クラスパスやコンパイラオプションなどのオプションを表示する。デフォルトの設定は、Max/MSPとJavaの標準インストールを利用して動作する必要がある。もし、Javaコンパイラのデフォルトのパスが不適切であったり、または異なるJavaコンパイラを使いたい場合、新しいコンパイルコマンドを選ぶためのブラウズボタンを使うことができる。この設定はアプリケーションを終了した後でも、再び変更をするまで記憶される。
.javaクラスが見つからず、.classバイトコードの逆アセンブリが試みられる場合、わたしたちは現在JODE libraryを逆コンパイルの制御に使用する。もし、クラスが.JARからコンパイルされたものであるならば、自動生成されたソースコードはコンパイルの前に新しいファイルに保存されるべきである。
コンパイルが成功した後は、以降にクラスのインスタンス化がされたもののみに変更が反映される。クラスの内容が変更される前にインスタンス化されたすべてのオブジェクトは、古いクラスのインスタンスとして機能し続ける。それゆえ、もし、max.classloader.fromdiskオプション(Configurationの章で説明するように)を有効にしていなければ、クラスファイルは、インスタンス化されたオブジェクトが"_zap"メッセージを受け取ったときのみ再び読み込まれる。一方、Maxは、前回のインスタンス化のときにキャッシュされたクラスのバージョンを使用する。もし、max.classloader.fromdiskが有効であるならば、すべての新しいクラスのインスタンスはディスクの.classファイルのディレクトリからロードされる。開発者にとっては便利だけれども、新しいmxjオブジェクトを作成する際、実質遅くなってしまうので、このオプションはデフォルトではオフになっている。
Max上での編集環境が提供する他の良いツールは、クラスリファレンスウィンドウである。クラスをハイライトし、MacであればCommand-D、Windowsであれば、ctrl-Dを押すと、ポップアップのMaxクラスリファレンスウィンドウは、クラスフィールドやメソッドのブレークダウンを提供する。
他のクラスのソースにアクセスする方法は、quickieクラスを通すことによってである。quickieは引数としてクラスの名前をとる。たとえば、"mxj quickie MyClass"という新しいMaxオブジェクトを初期化し、それからbang信号を送るか、新しく作られたオブジェクトをダブルクリックするかしたとすると、エディタ環境はMyClassのコードをロードする。もし、MyClass.javaやMyClass.classが見つけられなかったとすると、エディタはテンプレートクラスを開く。このテンプレートクラスはMaxのsearch pathでどこにあるか見つけることができるQUICKIE_PROTOと呼ばれるファイルの内容で定義されている。デフォルトではQUICKIE_PROTOは以下のようなものである。
import com.cycling74.max.*;
public class MyClass extends MaxObject
{
public MyClass(Atom[] args)
{
declareInlets(new int[]{DataTypes.ALL});
declareOutlets(new int[]{DataTypes.ALL});
}
public void bang()
{
}
public void inlet(int i)
{
}
public void inlet(float f)
{
}
public void list(Atom[] list)
{
}
}
Configuring
java/max.java.config.txtファイルは、classpathやvirtual machineのオプションについてのmxjシステムのカスタマイゼーションを可能とする。ファイルを使用するための例は、ファイルの中のセミコロンで始まり、また終わるコメントに含まれている。そのラインをコピーして、セミコロンを除去し、構成に合うようにテキストを編集しよう。すべてのmax.java.config.txtは、maxスタイルのパスとして特定される必要がある。なので、MacとWindowsどちらを使おうとも、ディレクトリのフォーマットは、
volume:/path/to/dir
または
/path/to/dir
である。
system classpathの中でロードされたクラスは、動的にリロードされない。これらのクラスについては、Compilingページで述べたような"_zap"メッセージは有効でない。dynamic classpathの中のクラスについては、動的にリロードされる。もし、classpathに独自のディレクトリを追加している場合、システムエントリと対照的に、ダイナミックエントリを加える方がおそらく最良である。
利用できるオプションの簡単な概要は次の通り
max.system.jar.dir [directory] - システムクラスパスにディレクトリの中のすべてのJARを追加する
max.dynamic.jar.dir [directory] - ダイナミッククラスパスにディレクトリの中のすべてのJARを追加する
max.system.class.dir [directory] - システムクラスパスにディレクトリを追加する
max.dynamic.class.dir [directory] - ダイナミッククラスパスにディレクトリを追加する
max.jvm.option [option] - JVMオプションをセットする
max.classloader.fromdisk [val] デフォルトではvalは0であるが、これはハードドライブからクラスを一度読み込んだら、RAMにキャッシュされることを意味する。もしvalが1であれば、クラスの新しいインスタンスはすべてハードライブから新しくロードされる。1を使うことは、新しいオブジェクトのインスタンスを素早く作ることができるが、0を使うことは新しいmxjクラスのコードを開発するときに便利でありうる。(チュートリアルにはこのように記述してあるが、1と0は逆であると思われる)
Tutorial01
Maxの互換性のあるJavaクラスは、すべてのその種のオブジェクトに共通する変数やメソッドを定義したプロトタイプである。
mxjプログラムを作成する際には、Maxとのインターフェースとなるメソッドなどを含んだMaxObjectと呼ばれるクラスを拡張する。
新しいMaxオブジェクトを作成したとき、空のオブジェクトボックスにmxj MyClass
とタイプすることによって、mxjは二つのオブジェクトのインスタンスを作成する。一つは、MyClassのJavaオブジェクト、もう一つは、Javaオブジェクトを格納するためのmxjオブジェクトである。
最初のexampleはとても典型的なものである。オブジェクトのインスタンスが作成されたときに、Maxコンソールに"hello world!"と出力される。これがHelloWorld1Javaクラスのためのコードである。
import com.cycling74.max.*;
public class HelloWorld1 extends MaxObject {
public HelloWorld1() {
post("hello world!");
}
}
短くて簡単なものである。まずは一番上のimport文から、コードのブロックを詳細に説明しよう。最初の行は、すべての基本的なクラスやmxj APIのメソッドを使用することを許可するものである。他にもJava APIのクラスや、サードパーティ製のライブラリも読み込めるが、このクラスにとって必要なものすべてはCycling '74が提供するmax.jarパッケージに含まれている。
次のブロックでは、このクラスはMaxObjectのサブクラスであることを定義している。
public class HelloWorld1 extends MaxObject {
...
}
classの宣言の前で定義されているpublic修飾子は、単純に、プログラムの外からこのクラスにアクセスできることを意味している。Max上で存在するために、JavaクラスはMaxObjectのサブクラスである必要がある。この関係は、extendsキーワードで定義される。サブクラスはスーパークラスのすべてのpublic変数やメソッドを継承する。MaxObjectはJavaコードとMaxのやりとりをする多くのメソッドが含まれているが、このexampleでは、MaxコンソールにStringアーギュメンツを出力するpostメソッドを使用するだけである。
public HelloWorld1() {
post("hello world!");
}
上記のコードは HelloWorld1 クラスのコンストラクタである。このメソッドは、新しいクラスのオブジェクトのインスタンスが作成されるたびに呼ばれる。Maxプログラミング上では、このメソッドは、HelloWorld1の引数が与えられた新しいmxjオブジェクトが作られたときに呼び出される。このコンストラクタは、クラスと同じ名前にする必要がある。
このHelloWorld1クラスのコンストラクタメソッドは単純なもので、postを呼び、Maxウィンドウに"hello world!"と出力するようにするものである。
下の図からわかるように、mxjを必要とする。この時点では、HelloWorld1.javaを開いて、"hello world!"を何か他のおもしろいものに置き換え、それから改善したHelloWorld1クラスをコンパイルし、インスタンス化することを確認するといったことを、やってみる価値があるかもしれない。
Tutorial02
Maxオブジェクトが作られた後、Maxオブジェクトは、メッセージを送信したり受信したりすることができる。Maxオブジェクトは他のオブジェクトと整数、浮動小数点数、sysmbol型でつくられたメッセージを送ることによって通信できる。mxj APIは、これらの3つのデータタイプを一つのユニットで表現できるAtomクラスを定義している。このAPIは、Atomから、または、Atomへ、主な8つのプリミティブなタイプ(byte, short, int, long, float, double, boolean, char)との変換を行う便利なメソッドを提供している。
メッセージを送信・受信するために、オブジェクトはインレットとアウトレットを持たなければならない。後のチュートリアルで、インレットとアウトレットの数を特定することを学ぶが、今は同じコンストラクタのままで、MaxObjectOクラスのデフォルトの1つのインレット、2つのアウトレットを用いる。
HelloWorld1クラスが、Maxの世界とよりインタラクションできるように、いくつかのメソッドを追加しよう。HelloWorld2と名前を変える。(コンストラクタ名と、.javaテキストファイルは、新しいクラスの名前と一致している必要があることに注意しなければならない。)
import com.cycling74.max.*;
public class HelloWorld2 extends MaxObject {
public HelloWorld2() {
post("hello world!");
}
public void errpost() {
error("hello error!");
}
public void hello() {
outlet(0, "world!");
}
public void cycling() {
outlet(0, 74);
}
}
mxjオブジェクトがJavaクラスのインスタンスを作成したときに、mxjは、Javaクラスが定義したpublicメソッドの名前を使って、サポートされたメッセージのテーブルをビルドする。新しいMaxオブジェクトボックスを作って、その中にmxj HelloWorld2
とタイプしたとき、mxjオブジェクトは、Javaクラスを分析し、内部に"hello"、"cycling"、そして"errpost"というメッセージを受けとる準備をおこなう。コンストラクタについては、このプロセスから免除されているので、このexampleの場合、"HelloWorld2"メッセージを受けとることはない。後のチュートリアルで、いくつかこれを免れる特別な関数のメソッド名と出会うだろう。メソッドを、mxjが入力メッセージにマップしないようにしたい場合、単純にpublicの代わりにprivateとして宣言すればよい。
Max上で、"errpost"メッセージを受け取ったとき、mxjオブジェクトは、MaxObjectのerrorメソッドを呼びだすerrpostメソッドをコールする。errorは、標準Maxエラープレフィックスがコンソール出力の前の部分に配置されていることを除いて、postと似たようなものある。
helloとcyclingメソッドはMaxObjectのoutletメソッドの使い方を説明している。outletの最初の引数は、どのアウトレットを使うか特定する整数のインデックスである。アウトレットは左から右へナンバーがふられており、0から始まる。なので、これらのケースでは、どちらもデータは一番左のアウトレットから出力される。
outletメソッドの2つめの引数は、異なったフォームの数を扱うことができる。helloメソッドでは、第2引数として、Stringを渡しており、オブジェクトは、Max上で"hello"メッセージを受けとったとき、world!というsymbolを出力する。cyclingメソッドでは、第2引数は整数であり、"cycling"を受けとったとき、オブジェクトは整数74を出力する。
いくつかのプログラミングの専門用語を定義しよう。メソッドのsignatureは、その引数の名前とタイプと配列のことである。たとえば、上記のhelloメソッドで使ったoutletメソッドのsigunatureは、outlet(int, String)であるが、cyclingメソッドで使っているoutletメソッドのsignatureはoutlet(int, int)である。C言語と違って、2つのメソッドが同じ名前を持っている必要はなく、Javaは、異なるシグネチャ(それらの引数は数やタイプが異なる)の特定の名前のメソッドを区別することができる。このテクニックはオーバーローディングと呼ばれており、気をつけて使えば、コードを書きやすく、また読みやすくする。outletメソッドはオーバーロードされたメソッドの例であり、下記のものは適切に呼び出される、シグネチャのすべてのリストである。
outlet(int, Atom)
outlet(int, Atom[])
outlet(int, boolean)
outlet(int, boolean[])
outlet(int, byte)
outlet(int, byte[])
outlet(int, char)
outlet(int, char[])
outlet(int, double)
outlet(int, double[])
outlet(int, float)
outlet(int, float[])
outlet(int, int)
outlet(int, int[])
outlet(int, long)
outlet(int, long[])
outlet(int, short)
outlet(int, short[])
outlet(int, java.lang.String)
outlet(int, java.lang.String, Atom[])
すべてのoutletメソッドは共通の基本的な関数をもっている。しかし、それらは、出力したいデータをどのように制御するかによって異なる。outletは、コンストラクタの中から呼び出されるべきではない。なぜなら、コンストラクタメソッドの実行が完了するまで、オブジェクトは完全に成形されておらず、データを送る準備ができていないからである。
もし、実験してみたいならば、メソッド名を変えたり、それが出力するものを変えてみたり、再コンパイルして新しいクラスがMax上でどのようにふるまうかを見ることができる。
Tutorial03
Maxオブジェクトが送信したり受信したりできるメッセージのタイプをもっと知るために、再びクラスを変更して、HelloWorld3と呼ぶようにしたい。
import com.cycling74.max.*;
public class HelloWorld3 extends MaxObject {
public void bang() {
post("hello bang!");
outletBang(0);
}
public void loadbang() {
post("welcome to the patch!");
}
public void inlet(int i) {
post("hello integer " + i + "!");
outlet(0, i);
}
public void inlet(float f) {
post("hello float " + f + "!");
outlet(0, f);
}
public void list(Atom[] a) {
post("hello list " + Atom.toOneString(a) + "!");
outlet(0, a);
}
public void anything(String s, Atom[] args) {
post("hello anything " + s + " " + Atom.toOneString(args) + "!");
outlet(0, s, args);
}
}
まず、最初に気がつくことは、もはやコンストラクタメソッドを持っていないことである。もし、デフォルトの1つのインレットと2つのアウトレットで事足り、インスタンス化されたときに行いたい処理がなければ、コンストラクタを持つ必要はない。
このクラスは、それぞれ特有のMaxメッセージを出力する6つのメソッドを持っている。bangメソッドはオブジェクトがbangを受け取った時に呼ばれる。このメソッドは、Maxコンソールにメッセージを出力し、それから、前回では見られなかった、オブジェクトの第1アウトレットからbangを送信するMaxObjectであるoutletBangメソッドが呼ばれている。
loadbangメソッドは、ユーザが、このクラスのオブジェクトを含んだパッチを開いた時に呼ばれる。このメソッドはコンストラクタが呼ばれた後に呼ばれるので、安全に内部のoutletメソッドを呼ぶことができる。このサンプルでは、単に歓迎のメッセージを送出している。
次の2つのメソッドの実装は、どちらもinletという名前である。**inlet(int)とinlet(float)**メソッドは単一の整数、または浮動小数点数をそれぞれ受けとる。整数を受けとった時、入力された整数iと、合成されたメッセージがMaxコンソールに出力され、iが第1アウトレットから出力される。また、浮動小数点数を受けとったときは、同様の処理が実行される。
次のlistメソッドは、オブジェクトのインレットにlistが送られたときに呼ばれる。引数aはAtomの配列である。また、このサンプルでは、AtomクラスのメソッドのひとつであるAtom.toOneString()を使用している。このメソッドは、Maxコンソールから出力するために、Atom配列を一つの長いString型に変換するものである。
最後のメソッドは、anythingメソッドである。このメソッドは、このクラスのなかで他で宣言されたpublicメソッドの名前(この場合は、"bang", "inlet", "list")でないsymbolが、最初の要素であるメッセージを受け取ったときに呼ばれる。
String s は最初のsymbolで、args配列はどんな配列長でも許容されるAtom配列である。
もしクラスがinlet(int)を定義せず、inlet(float)を定義していた場合、入力された整数は、inlet(float)にリダイレクトされる。そのどちらのメソッドも定義されていなかった場合、mxjは、**list(Atom[])が存在していれば、それを呼びだす。もし、存在していなければ、mxjはanything(String, Atom[])を呼びだす。もし、listが受けとられて、listメソッドが定義されていなければ、mxjは、anything(String, Atom[])**を呼び出す。このとき、Stringには"list"がセットされる。
Tutorial04
Maxの(+)オブジェクトと同様の機能を持つexampleクラスを作ることによって、Javaについての研究をつづけよう。plusオブジェクトは2つのインレットを持っていることを思い出したい。右のインレットはaddendにセットすることに使われ、左のインレットは主要な値の入力と加算されたものの出力のトリガーに使われる。
import com.cycling74.max.*;
public class Plus1 extends MaxObject {
private int addend = 0;
public Plus1() {
declareInlets(new int[]{DataTypes.INT, DataTypes.INT});
declareOutlets(new int[]{DataTypes.INT});
}
public void inlet(int i) {
if (getInlet() == 0) {
outlet(0, i+addend);
} else {
addend = i;
}
}
}
import文とクラスの宣言のあと、整数型として、変数addendが宣言されている。privateキーワードは、クラスの外から、この変数にアクセスできないことを意味する。addendは、インスタンス変数であり、作成されたPlus1クラスのそれぞれのコピーが、addendのインスタンスを内部に保持している。また、1度だけ変数領域の割り当てを行い、全てのクラスからアクセスできる変数を定義することも可能である。このクラス変数についてのexampleについては、のちのチュートリアルで見ることができる。しかし、ユーザはおそらくそれぞれ異なったパッチのパートで、別のaddendをもった、それぞれ異なるPlus1オブジェクトを持ちたいだろうから、この場合インスタンス変数が割り当てられている。ちなみに、addendにはデフォルトでは0が割り当てられる。
この変数の宣言につづくコンストラクタでは、インレットやアウトレットの数を変更するexampleを、はじめて目にすることができる。declareInletsとdeclareOutletsメソッドは、それぞれ引数として整数型の配列をとる。(実際には、アウトレットの数は、declareOutletsに、渡した整数型の配列の中のアイテムの数よりも1つ多くなる。これは、特別なタイプの変数であるattributeの標準出力メソッドの調整のためであり、のちのチュートリアルで言及する。
このexampleでは、declareInletsと、declareOutletsメソッドが呼ばれたところで、右の括弧の中で新しい配列を作っていることがわかると思う。また、その配列の中身は、DataTypes.INTを使って初期化されていることに気づくだろう。それぞれのインレットとアウトレットは、特定のデータタイプと関連づけられ、パッチングシチュエーションの混乱を防ぐことができる。たとえば、もし特定のアウトレットは、整数型しか出力したくない場合、開発者はDataTypes.INTを宣言する。そうすると、Maxは、そのインレットとの間に作られた接続を許可しないようになり インレットはfloat(Integerでは?)を受けつけるのみとなる。
DataTypesクラスは、インレットとアウトレットの宣言に使うことができる、5つの異なるデータタイプを定義している。それぞれは、HelloWorld2チュートリアルで説明したメッセージのタイプのうちの1つと一致している。DataTypes.INTは整数型、DataTypes.FLOATは、浮動小数点数型、DataTypes.LISTはlist型、DataTypes.MESSAGEはメッセージ型、DataTypes.ALLは、どのような型のメッセージもインレットまたはアウトレットで、受け入れ、あるいは送信するようになる。DataTypes.ALLを選択するのが最も汎用性が高いが、Plus1は整数型でのみ動作するように設計されているので、DataTypes.INTを使用している。加えて、パッチングエラーを防ぐため、整数型のみのインレットやアウトレットを使うことは、Maxが整数の送受信に関連したオーバーヘッドを最小化することを可能にする。それゆえ、DataTypes.INTのような、タイプづけを使用することは、DataTypes.ALLでタイプづけをしないものよりも効果的であり、できるかぎり使用すべきである。
コンストラクタの他は、このクラスは、2つのそれぞれのインレットから入力された整数を処理するinletメソッドが1つあるだけである。どのインレットが使用されたか調べるために、getInletメソッドが使われている。getInletは呼びだされたインレットに適合するインデックスを返す。上記のメソッドでは、もし左側のインレットに整数が入力された場合、左のアウトレットから合計値が出力され、右のインレットが使われた場合、入力値はaddendに格納される。
Tutorial05
Plus1は、Maxのビルトインオブジェクトである(+)オブジェクトの基本的な振る舞いをしていた。しかし、このexampleには、いくつかの違いがある。ビルトインの+オブジェクトをただしく機能的に複製するために、このクラスではaddendにセットするインスタンス用の引数を許可するようにしなければならない。また、わたしたちのクラスでは、整数、浮動小数点モードのどちらでも機能するようにしなければならない。たとえば、もしインスタンス引数があった場合、それが浮動小数点であればそのオブジェクトは浮動小数点モードとしてふるまう。そうでなければ、整数モードとして動作し、入力されたfloatは丸められる。この機能が一般的でないように思えるなら、下記のコードを読む前に、(+)オブジェクトのヘルプファイルを、少しでもよいので、開いて実行してみてほしい。
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);
}
}
}
}
まず、注意したい変更は、かつてはintであったaddend変数にはAtomが使われていることである。わたしたちが、Atomを使用したのは、整数でも浮動小数点でも表現することができ、このオブジェクトのデュアルモードオペレーションが便利であるからである。コンストラクタメソッドを直接呼ぶことによって、クラスのインスタンスを作成することを許可するクラス、Atomクラスは、APIで提供されているAtom.newAtomファクトリメソッドの1つを呼ぶことを必要とする。Atom.newAtom(int)メソッドは、デフォルトで整数0として表現するように、addendを初期化するために使われている。
2つめの変更は、コンストラクタメソッドは引数としてAtom配列をとっていることである。Maxオブジェクトボックスの中のクラス名の後に付加されたインスタンスの引数は、Atomの配列としてコンストラクタにわたされる。つまり、もしユーザが、Max上で、"mxj Plus2 74"とタイプされた新しいオブジェクトボックスを作った場合、args配列は、74という値の単一の整数であるAtomを格納している。
ここでは、インレットとアウトレットの定義に使用したメソッドが異なる。declareIO(int, int)は、ふたつの整数をとり、DataTypes.ALLの多くのインレットとアウトレットを作成する。これらのインレットとアウトレットを宣言したあと、Plus2クラスのコンストラクタメソッドは、args配列の配列長は0以上であるかを確かめようとする。Javaでは、配列の大きさはオブジェクトを配列のように取り扱うことによって確認でき、上記のコードで行っているように、lengthフィールドでアクセスすることができる。もし、インスタンス化引数が与えられていなかった場合、args配列の大きさは0となり、プログラムフローは、addendはデフォルトの整数0とし、コンストラクタメソッドを抜け出す。
しかしながら、もし、少なくとも一つ以上のAtomが、配列に存在する場合、さらなるテストが行われる。isInt()とisFloat()メソッドは、argsの最初の要素が整数か、または浮動小数点数として表されているのかを調べるのに使われており、それによって最初の要素はaddendに格納される。
このクラスでは、どちらのタイプの数値の入力でも受けいれることができるので、2つのinletメソッドが必要である。if-elseロジックは、入力を受けとったインレットによって、実行のフローを分岐させる。もし、右のインレットに数値が来た場合、新しいAtomが作られ、addendに代入される。もし、左のインレットに数値が来た場合、outletメソッドは、入力された値と、addendの加算した値を、左のアウトレットから出力する。どちらのinletメソッドでも、addendが整数型なのか浮動小数点数型なのかを調べるisIntメソッドが使われており、もし、必要であるならば、入力値は、加算またはaddendに格納される前に、適切な型にキャストされる。
C言語プログラマは、addendによって表された値を変更するたびに、新しいAtomが作られなければならないことを、珍しいことに思うかもしれない。Atomクラスは、イミュータブルである。つまり、一度インスタンスが作られたら、値を表す型は変えることができない。イミュータブルであることに、クラスを設計する上で十分な理由があることについて学ぶために、Joshua Blochの優れた著書Effective Javaを参照してみてほしい。
このPlus2クラスにはなくて、ビルトインの(+)オブジェクトには存在する細かな機能がまだいくつかある。もし、Plus2のインスタンスが、左のインレットにbangを受けとったら、最期の結果が再び出力される。そして、listを受けとった場合、オブジェクトは、2つ目の要素が右のインレットに、最初の要素は左のインレットに送られてきたかのように値を受けとる。理解を確かめるために、あなたは、この機能の不足を補足するためPlus2を編集してみたくなるかもしれない。
Tutorial06
autocount1 - Attributes
このexampleでは、Maxの世界とクラスのなかの変数を簡単に結びつけるattributeシステムについて紹介する。
import com.cycling74.max.*;
public class autocount1 extends MaxObject {
private int count=0;
public autocount1() {
declareAttribute("count");
}
public void bang() {
count++;
outlet(0, count);
}
}
count変数は、通常、他で定義される整数型の変数と同様に定義されているが、このクラスのコンストラクタでは、declareAttributeメソッドに、文字列としてこの変数が渡されている。そうすることによって、countは、自動的に便利な機能を与えるattributeとして登録される。
attributeに提供される主な機能は、自動的、内部的に、Maxパッチ上で、変数の値を取得・設定することのサポートである。
autocount1オブジェクトに"count 8"メッセージを送った場合、count変数に8が代入される。
また、"get count"メッセージを送った場合、オブジェクトの右側のoutletから、8が出力される。
たとえば、setメソッドで、変数への入力値に制限を設けたい場合、また、getメソッドで、単に変数の値の取得をするのではなく、追加の情報を付加したい場合など、attributeに独自のsetter、getterを定義したい場合がある。そのときは、declareAttributes(String, String, String)をコールする必要がある。第2引数、第3引数には、それぞれ、setterメソッドとgetterメソッドをStringで指定する。nullを指定した場合、デフォルトのget、setが実行される。setterメソッドを有効にするには、戻り値の型はvoid、getterメソッドには、**Atom[]**を指定する。
他の主な機能は、インスタンス作成時の引数から、attributeのsetメソッドにアクセスできることである。
"@count"を含む新しいautocount1オブジェクトを作った場合、それに続く値が、コンストラクタが実行されたあとにcountのsetメソッドに渡される。
setterメソッドはコンストラクション時に呼ばれること、また、コンストラクタと同様に、setterメソッドはオブジェクトのoutletからデータを出力するべきでないことに注意しなければならない。
Totorial07
このクラスでは、autocount1を自動化する。前のクラスでは、カウントを1つずつ増加させ、その結果を出力するためにbang入力を要求していたが、下記のこのクラスでは、私たちは、ユーザによってセットされた間隔で自動的にbangメソッドが呼び出されるclockを作成するために、MaxClockクラスを使用する。
import com.cycling74.max.*;
public class autocount2 extends MaxObject implements Executable {
private MaxClock clock;
private int count=0;
private float interval=400.f;
public autocount2() {
declareAttribute("count");
declareAttribute("interval");
clock = new MaxClock(this);
}
public void inlet(int i) {
if (i==1)
clock.delay(0);
else
clock.unset();
}
public void execute() {
bang();
clock.delay(interval);
}
protected void notifyDeleted() {
clock.unset();
}
public void bang() {
count++;
outlet(0, count);
}
}
まず、最初に気がつくのは、autocount2は、MaxObjectの継承だけでなく、Executable interfaceの実装も行われていることである。Javaでは、Interfaceは、関連のないオブジェクト同士が、お互いにやりとりするために使う装置である。あなたは任意のクラスで実装することができるinterfaceを使用する。このクラスがExecutableを実装するために必要なことは、execute()メソッドを定義することだけである。
このケースでは、新しいMaxClockを作るときのコンストラクタの中のExecutableの中のクラスであるという事実を使用する。MaxClockクラスのためのコンストラクタは、引数としてexecutableを必要とする。それから、clockが実行される時間に、MaxClockは、コンストラクタが実行されたときに渡されたExecutableオブジェクトの中のexecuteメソッドを呼びだす。この場合では、わたしたちは、"this object"の簡略表記法であるthisキーワードを渡している。つまり、新しいautocount2オブジェクトは、Executableとして、自分自身をMaxClockコンストラクタに渡している。そして、MaxClockが何か行うときに、それがautocount2オブジェクトのexecuteメソッドを呼びだす。
MaxClockは、delay(double)メソッドを呼ぶことによって、先のある時点で実行されるように指示することができる。deleyメソッドのdoubleは、clockが実行されるまで待つミリ秒を表している。上記のクラスでは、もしtoggleがインレットに接続されているときのように、mxjオブジェクトが1を受けとったら、コードはclock.delay(0)を呼びだす。
executeメソッドは、それからすぐに呼び出される。ここでは、count変数の値を増加させ、この結果を出力するbang()を呼び出しており、その後、clock.delay(interval)を呼び出しており、それはexecuteメソッドが400ミリ秒後に呼ばれるようにしている。400がinterval変数のデフォルトの値であるが、わたしたちはattributeとして宣言しており、ユーザはintervalメッセージとともに新しい値をセットできる。
わたしたちは、このクラスで使用しているnotifyDeletedメソッドについても紹介する。疑わしいかもしれないが、Javaクラスを含んだmxjオブジェクトが、Maxパッチ上から削除されたときに呼び出される。このメソッドの典型的な用途は、mxjオブジェクトが生成されてから、動作している間に作られたすべてのオブジェクトの、宙ぶらりんな状態をきちんと処理するためである。このケースでは、終わりを適切に処理すべきなのは、MaxClockのみであり、もしそれが実行されていた場合、オブジェクトのexecuteメソッドを繰り返し呼び出し続け、bangメソッドが要求するアウトレットがもはや存在しないとき、望ましくない結果を生み出す。clock.unsetを呼び出すと、MaxClockは次に実行する予定だったexecuteを呼び出すことを停止するようになる。
Tutorial08
ユーザから見ると、最近のOSは同時に多くのプログラムを実行することができる。実際には、OSに確保されたそれぞれのCPUは、実行するスレッドを一つだけ操作する。この同時実行のイリュージョンは、多くのアクティブなスレッドを、すばやく切り替えることによって達成している。どのスレッドにオーダーをセットするかのスケジューリングの分析と設計は、コンピュータサイエンス研究の多産な分野である。
1つのプログラムは、それ自身に複数の実行スレッドを構成している。たとえば、Webブラウザは、あるスレッドはネットワーキングを制御し、他はユーザの入力を扱っている。これらの分離されたスレッドでプログラムを同時実行することは、他のスレッドが(Webページのコンテンツを検索するなど)リクエストが完了するのを待つ間、(例えばプリファレンスを設定するなど)プログラムがユーザの入力の処理することを可能にする。
mxjが使われているとき、Max環境からの入力や出力は、2つのスレッドのうち1つを使うことができる。メインスレッドは、スクリーンにレンダリングすることや、マウス、キーボードイベントを処理するなど、少なくとも時間というものが重要である仕事を制御する。このschedulerスレッドはmidi、クロックオブジェクト(metro, delayなど)などの時間に精密なデータを制御する。
下記のコードはclassディレクトリの中に供給されたWhichiThread exampleからである。
import com.cycling74.max.*;
public class WhichThread extends MaxObject implements Runnable
{
private static final String SCHEDULER = "SCHEDULER";
private static final String MAIN = "MAIN";
private static final String UNKNOWN = "????";
public void bang() {
if(MaxSystem.inTimerThread())
outlet(0,SCHEDULER);
else if(MaxSystem.inMainThread())
outlet(0,MAIN);
else
outlet(0,UNKNOWN);
}
public void run() {
this.bang();
}
public void spawnThread() {
Thread t = new Thread(this);
t.start();
}
}
bangメソッドは、実行するスレッドを決定するMaxSystemの、inTimerThreadやinMainThreadメソッドがどのように使われるかを表している。それぞれの実行スレッドのための可能性は、異なる出力を生み出している。WhichThread.helpパッチを見てみるのがよい。
もし、helpパッチのどちらのbangもクリックした場合、bangメソッドはメインスレッドを呼び出し、"MAIN"が出力される。もしオーバードライブが有効であるならば、helpパッチのmetroを開始すると、schedulerメソッドからbangメソッドが呼び出され、"SCHEDULER"が出力されるだろう。もしオーバードライブをオフにしていた場合、"MAIN"が出力される。
WhichThreadは、クラスが、自身のスレッドを作る1つの方法を表している。クラスはRunnableを実装していることに注目してほしい。Runnableは、前のチュートリアルで議論したExecutableインターフェースのようなもので、スレッドベースなクラスのための、共通呼び出しフレームワークを供給するために使われている。Runnableを実装するために、クラスはpublicのrunメソッドを含む必要がある。runメソッドは、クラスが作られた時に実行される。上記のケースでは、spawnThreadが呼ばれたとき、WhichTreadオブジェクトそれ自身が、"this"キーワードでコンストラクタメソッドに渡されることによって、Threadクラスの新しいインスタンスが作られる。次の行では、新しいThreadのstartメソッドが呼ばれている。これはThreadがどのように実行を開始するかを伝えるものである。startメソッドは、コンストラクタ実行とき渡されたRunnableのrunメソッドを呼びだす。わたしたちは、オブジェクトのなかで自分自身を渡すので、WhichThreadのrunメソッドが呼びだされ、それからbangメソッドを呼ぶ。なので、bangがメインでもスケジューラスレッドでもないところから呼びだされた場合、"????"が出力される。
もし、ヘルプパッチの"spawnThread"メッセージをクリックしたとき、実際には"????"がオブジェクトのアウトレットから出力されることに気づかされる。しかしながら、最初のWhichThreadオブジェクトの出力を受けとっている右のコンストラクションはbangを発砲し、他のWhichThreadオブジェクトのbangを得ている。"spwanThread"メッセージをクリックした時、アクションのインスタンスを生成したスレッドは、mainでもschedulerスレッドでもないので、2つ目のWhichThreadオブジェクトも"????"が出力されることを期待するかもしれない。これは、MaxObjectのoutletメソッドは、Maxのフィールドにはmainまたはschedulerスレッドでしかメッセージを出力しないからである。これら2つのスレッドのどちらからでもなく呼ばれたoutletは、mainスレッドに回収される。
他のスレッドから、いつでもスレッドを中断できることを理解することが重要である。例えば、OSがmainスレッドの代わりにschedulerスレッドに切り替えると決めたとき、mainスレッドは中断され、schedulerスレッドがそれに代わる。mainスレッドが再開されると、mainスレッドはどこでそのままにされているかを確かめる。これは、ある不安定な状況につながりうる。
次のクラスを考えてみる。
public class threadProblem extends MaxObject {
private int[] data = new int[10];
public void bang() {
int size = data.length;
for (int i=0;i size;i++) {
data[i] = i;
}
}
public void inlet(int i) {
data = new int[i];
}
}
オブジェクトの中に送られたintは、与えられた配列長の新しい配列を作成する。bangがその数の配列に値を代入する。しかしながら、もしmainスレッド内で(ユーザの入力で引き起こされる)は、bangメソッドが実行されているとき、または、schedulerスレッドで整数が入力されたとき、中断された場合、何が起こりうるのか考えよう。もし、前回の配列の数よりも入力が小さかった場合、新しい配列の長さまでのfor文のループの終わりを超えたときに、mainスレッドに渡された実行のフローの後、短く例外が投げられる。もし入力された数が大きかった場合は、何の例外も投げられない。しかし、古い配列長を越えた配列の値、または、forループですでにセットされた値は初期化されない。どちらの方法の結果も歓迎されない。不運な実行のフローは、不確定な状態でデータを放置するので、クラスをたしかに設計することが重要である。
Tutorial09
mxjプログラマーにとって利用できるツールのセットはコードのpublicライブラリを使うことによって拡張することができる。フリーコードを使用できることは、Javaプラットフォームの大きな強みである。このことを念頭において、mxj APIは可能な限り簡単に他の人の仕事を利用できるように設計されている。
ほとんどのライブラリは.JARファイルとして配布されている。たとえば、net.mail.sendやnet.main.recvオブジェクトを使用できるようになるためには、JavaMailがインストールされていなければならない。これが書かれたときのライブラリの現在のバージョンは1.3.1である。ある場合は、JavaBeans Activation Frameworkをインストールしなければならない。ファイルをダウンロードして、解凍したのち、mail.jarとactivation.jarをあなたのjar/libディレクトリにコピーしてください。(あるいは、Configureページで説明しているように、max.java.config.txtファイルを編集することもできる)
readmeやライセンスファイルなどの集まりに加えて、多くのライブラリはJavaのjavadocユーティリティによって生成されたAPIドキュメントが付属している。JavaMailの場合、これらのdocsはdocs/Javadocに格納されている。それらはあなたがnet/mail/send.javaまたはnet/mail/recv.javaのなかのコードを調べたいときに便利である。外部ライブラリを参照するクラスをコンパイルするように、新しい.JARファイルを含むように、コンパイラのクラスパスを変更する必要がある。たとえばもし、classes/net/mailディレクトリにいるのであれば、net.mail.recvクラスのコンパイルはコマンドプロンプトに以下のような入力を行うことで達成できる。
javac -classpath "../../../lib/max.jar;../../../lib/mail.jar" recv.java
Tutorial10
このkeepクラスは、パッチングのセッション間での、シンプルなデータの伝播についての例を提供する。
public class keep extends MaxObject {
private Atom[] stuff = null;
private MaxPatcher patch = getParentPatcher();
private MaxWindow window = patch.getWindow();
boolean autobang = true;
keep() {
declareIO(1,1);
declareAttribute("autobang");
setInletAssist(0, "(anything) whatever is input is stored and saved with the patch");
setOutletAssist(1, "info outlet");
setOutletAssist(0, "(anything) outputs whatever is stored on bang");
}
private void setStuff(Atom[] a) {
window.setDirty(true);
stuff = a;
}
public void inlet(int i) {
setStuff(new Atom[] {Atom.newAtom(i)});
}
public void inlet(float f) {
setStuff(new Atom[] {Atom.newAtom(f)});
}
public void list(Atom[] a) {
setStuff(a);
}
public void anything(String msg, Atom[] a) {
setStuff(Atom.newAtom(msg, a));
}
public void bang() {
outlet(0, stuff);
}
public void loadbang() {
if (autobang)
bang();
}
public void save() {
embedMessage("list", stuff);
}
}
keepはstuffの中のAtomの配列を維持する。クラスのインスタンスが新しい入力を受け取ったとき、その値はstuffに保持される。このメソッド
ユーザがパッチを保存したとき、Maxはすべてのmxjオブジェクトでsaveメソッドをコールする。デフォルトではこのメソッドは何も行わないが、上記のクラスのようにオーバーライドされた場合、embedMessageメソッドを使用することができ、パッチのファイルにメッセージを埋め込むことができる。そしてそれはディスクからロードされた新しいオブジェクトに送られる。
keepクラスのsaveメソッドのなかで、embedMessageはstuffを引数に"list"メッセージを埋め込んでいる。パッチがディスクからロードされたとき、オブジェクトはインスタンスされたとき、すぐにこのメッセージを受け取る。それからlistメソッドは、古い値のstuffを引数としてコールされ、パッチが最後に保存されたときに値が存在したのであれば新しいオブジェクトのstuffにセットされる。
loadbang、または、クラスのコンストラクタの場合のように、出力はパッチファイルに保持していたメッセージによってトリガーされるべきではない。そのようにすることは不安定になるリスクを帯びている。