このダイアログ中のリストからメソッドを選び、OKボタンを押したとき、ActionEvent が発生し、actionPerformedが実行され、新しいスレッドが走りだします。これは、 典型的といっていい、「新しいイベント・モデル」の実例ですが、実は、Edit-->Events メニューからメソッドが選択される場合のルーティンは、もう少し複雑になっています ので、解読してみて下さい。(ヒント:BeanBoxではなく、BeanBoxFrame のコードを 良く読んでください。)結局、このダイアログでは、HookupManagerクラスのhookupと いうstaticメソッドが呼び出されていることがわかります。
------------------------------------------------------------------------------- public class EventTargetDialog extends Dialog implements Runnable, ActionListener { ....... ....... EventTargetDialog(Frame frame, Wrapper sourceWrapper, Wrapper targetWrapper, EventSetDescriptor esd, Method listenerMethod) { ....... ....... okButton = new Button("OK"); okButton.addActionListener(this); add(okButton); ....... ....... } public void run() { int index = list.getSelectedIndex(); // Remove the current dialog, and put up a status line. removeAll(); Label status = new Label("Generating and compiling adaptor class"); add(status); status.setBounds(20, getSize().height/2, getSize().width-30, 25); repaint(); HookupManager.hookup(esd, listenerMethod, sourceWrapper, targetWrapper, methods[index].getMethod()); dispose(); } public void actionPerformed(ActionEvent evt) { if (evt.getSource() == okButton) { Thread th = new Thread(this); th.start(); } else if (evt.getSource() == cancelButton) { dispose(); } } ....... ....... } -------------------------------------------------------------------------------
HookupManagerクラスのhookupメソッドは、BeanBoxのイベント処理の中では、もっとも 重要といってよい仕事を受け持っています。ただ、すこし複雑で微妙なところがあり ますので、理解を容易にするため、ここまでの経過を、「新しいイベント・モデル」に 即して、振り返って見ましょう。
Edit-->Eventsメニューで、イベントの発生するButton Beanとその Listenerメソッド ActionPerformedが指定されます。赤いラバーバンドを張って、そのイベントを受け 取る Juggler Beanを指定し、EventTargetDialogで、ボタンが押されたときに、この Juggler Beanで呼び出されるメソッド stopを選びます。 BeanBoxでは、二つのBeanの間をこのようにwiringすることで、Beans相互のイベント 授受の関係を、ダイナミックに指定できるのです。
問題を簡単にするために、実行時に動的に関係が決まるのではなく、この二つのBeanの 間の関係が、あらかじめ決まっていたとしたら、どのようなコードが必要となるかを 考えてみましょう。次のようなコードが、まず、頭に浮かびます。細かいところは全部 省略しましたので、多少怪しいところがあるのですが、基本的には、ButtonBeanの側 では、JugglerBeanをActionイベントのListenerとして登録し、JugglerBeansの側では、 ActionListener インターフェースを実装する、具体的には、actionPerformedメソッド を定義して、その中でメソッドstopを呼ぶということになります。 この方法では、二つのBeansそのものの中に、イベントの定義が埋め込まれています。
------------------------------------------------------------------------------- class ButtonBean { ...... ButtonBean(JugglerBean juggler){ ...... addActionListener(juggler); ...... } ...... } class JugglerBean implements ActionListener { ...... actionPerformed(ActionEvent evt){ this.stop(); } ...... } class Exec { public static void main(String argv[]){ JugglerBean juggler = new JugglerBean(); new ButtonBean( juggler ); } } -------------------------------------------------------------------------------
JavaBeansでは、それぞれのBeanは、部品としてユーザに提供されます。部品の中に、 あらかじめ、「このボタンは、あの部品にイベントを送る」とか「この部品は、あの 部品から、これこれのイベントを受け取って処理する」といった情報が埋め込まれて いることは、ある意味では、部品としての「汎用性」を弱めることになります。部品と しての一般性を失わないように、ButtonBeanやJugglerBeanそのものの定義には、 手を加えずに、両者の外部にイベントをめぐる関係を定義する方法はないか考えて 見ましょう。自信のある方は、自分で考えて見てください。
実は、第三のクラスをイベントのAdapterとして置くと、この問題にうまく答えること が出来るのです。次のコードを見てください。これは、「ダイナミック=動的」でない ことを除いては、BeanBoxのイベント処理で使われている考え方と同一です。
------------------------------------------------------------------------------- class ButtonBean { ...... ...... } class JugglerBean { ...... ...... } class Adapter implements ActionListener { private JugglerBean target; public void setTarget(JugglerBean t) { target = t; } public void actionPerformed(ActionEvent evt) { target.stop(); } } class Exec { public static void main(String argv[]){ JugglerBean juggler = new JugglerBean(); ButtonBean button = new ButtonBean(); Adapter adapter = new Adapter(); adapter.setTarget(juggler); button.addActionListener(adapter); } } ------------------------------------------------------------------------------
新たに登場したAdapterクラスは、まず、ButtonBeanのActionEventのListener です。 ですから、ButtonBeanでActionEventが発生すると、AdapterクラスのactionPerformed メソッドが呼び出されます。この例でのactionPerformedメソッドの定義を見てもらえ ばわかりますが、このメソッドは、private変数target に設定されたObject上で、stop メソッドを呼び出すようになっています。ですから、ボタンが押された時に、 JugglerBean上でstopメソッドが実行されるようにするためには、target変数に、 JugglerBeanのインスタンスを設定する必要があります。 Adapterクラス中の、メソッド setTargetは、まさに、この為に用意されています。三つのクラスの関係を示すと、 次のようになります。
ActionEvent ButtonBean ----------------> Adapter ----------------------------> JugglerBean addActionListener() setTarget() / actionPerformed() ====> stop()
念の為に、どのように実行されるのかを、簡単に説明しておきましょう。 JugglerBean,ButtonBeanの定義には手を触れずに、それぞれ、そのインスタンスを生成 します。Adapterクラスのインスタンスも生成されます。最初にしなければいけない ことは、Adapterクラスのインスタンスの中で、actionPerformedメソッドが実行された ときに呼び出されるstopメソッドのターゲットをJugglerBeanのインスタンスに設定 することです。続いて、ButtonBeanクラスのインスタンスのActionEventのListenerと して、このAdapterクラスのインスタンスを指定します。
これで、もともとのBeansの定義はそのままでも、二つのBeansの外部からイベントを 関係づけることが可能であることがわかりました。しかし、まだ大事な問題が残されて います。今見た解法は、Beansの定義に手をつけることはないので、その意味では、 Beansの「一般性」の要請は満たしてはいるのですが、それはあくまでも二つのBeans間 の関係が、「あらかじめ」、決められているというstaticな想定で行われたものです。 このプログラムは、BeanBoxでのように、プログラムの実行中に、Beans間で 「ダイナミックに」、イベントの関係づけを行うというものではないのです。 それでは、BeanBoxでは、どのような処理が行われているのでしょうか? hookupは、まさにこの「ダイナミック=動的」な処理を担っているのです。
直接、hookuupメソッドの説明をする代りに、今まで見てきたAdapterクラスを利用する アプローチをダイナミックなものに変えようとした場合に、どのような問題があるのか を考えて見ましょう。大きくいって二つの問題があることがわかります。
第一は、Adapterクラスを利用することが抱える問題です。このクラスは、Beans間の イベントの関連付けで必要とされたものです。プログラムの実行中に、それまで存在 していなかった新しいクラスを生成することは、はたして可能でしょうか?
第二に、たとえ百歩譲って新しいクラスのインスタンスの生成が可能となったとし ても、それをどのように、実行中のプログラムの中で「ダイナミック=動的」に利用 することが出来るのでしょうか? 先のコードの中には、次のようなシーケンスが あります。
adapter.setTarget(juggler); button.addActionListener(adapter);ここで、adapterは、プログラムの実行中に生成された新しいインスタンスだと考えて ください。こうしたコードと等価なコードを、しかも、一般性を失わない形で、 はたして作れるのでしょうか?
第一の問題に対する解答は、次のようなものです。 「プログラム中で新しいクラスが必要になるのなら、そのクラスを生成するJavaの ソースプログラムを生成するプログラムを準備しておいて、必要なクラスのソースを ファイルに書き出して、プログラム中でコンパイルしてクラス・ファイルを作り出せば よい。」
クラス・ファイルが存在さえすれば、あとは、ClassLoaderを使えば、必要なクラスの インスタンスを作り出すことができるはずです。後で見るgenerateHookupメソッドが こうした役割をはたします。 こうしたことは、実は、JDK1.0でも、ある程度は可能でした。
ところが、第二の問題は、もう少し厄介です。JDK1.1では、こうした問題に対応する ために、java.lang.reflectという新しいパッケージを追加して、言語仕様を拡張しま した。クラスが与えられれば、そのクラス内で定義されている、コンストラクタ、 フィールド、メソッドの情報に、ユーザーがプログラムからアクセスできるように なりました。アクセスといっても、単にそれらの名前や型を知るばかりではなく、 クラスの変数に新しい値を設定したり、クラスのメソッドに引数を与えて起動する ことが出来るようになったのです。 introspector、reflectionといった、JDK1.1で導入された、 こうした新しい機能を知ることなしには、JavaBeansのプログラムを理解することは 出来ません。