JavaBeans入門
JavaBeansとは、BuilderとよばれるGUIツールで(vitsual)プログラミングができるようにするための標準のインタフェースを実装したJavaのクラスである。このJavaBeansの仕様にしたがってソフトウエアの部品をつくっておくことによって、これらを適宜組み合わせることによって、プログラミングすることができるようになり、再利用可能なソフトウエア部品を開発する環境を提供する。JavaBeansに関する情報は、
http://java.sun.com/j2se/1.3/ja/docs/ja/guide/beans/
にある。実際に使う場合には、ここからダウンロードできる。試してみたい人は、
http://java.sun.com/products/javabeans/software/bdk_download.html
から、Beans Development Kit (BDK)をロードする。Linux等では、platform independent BDKをロードすればJDK1.2がインストールしてあればつかうことができる。このパッケージには簡単なBuilderであるBeanBoxが含まれており、JavaBeansによるvisualプログラミングを試すことができる。これは非常に簡単なものなので、本格的に使いたい場合には市販のBuilderがいろいろと発売されている。
JavaBeansでは何ができるのか
Beansの場合は次のようなプログラミングが可能となる。
l
Builderのデザインシートの上に、お絵描きソフトの感覚でクラスを張り付けることができる。
l
変数値(プロパティ)をプロパティシートに羅列された個々のプロパティエディタ上で変更し、保存できる。
l
2つのクラスに対して特定の「イベントオブジェクト」の交換を指定することで連携させることができる。
l
デザインシートごと保存して、実行可能なJavaプログラムとすることができる。
JavaBeansは主に、GUIのプログラミングをする場合に使われるが、必ずしもGUIのプログラミングに限定されるものではない。
JavaBeans開発には次の2つの側面がある。
l
Beansライブラリを組み合わせてJavaプログラムを開発する。 すでに、JavaBeansとして開発されたボタンやアイコンなどを組み合わせて簡単にプログラムを作ることができる。
l
オリジナルのBeansライブラリを開発する。必要な部品は自分で開発して、これを組み込んだり、他の人に提供したりすることができる。
なぜ、Javaではこのようなプログラミング環境が可能なのか?これは、Javaシステムが機械語ではなく、JavaByteコードと呼ばれる仮想機械JVM(Java
Virtual Machine)のコードにコンパイルされ、JVMで実行されるということに大きく依っている。これが非常に柔軟なシステムすることを可能にしている。たとえば、ByteコードにコンパイルされたクラスファイルをJavaプログラムから読み、これを解析したり、ロードして動的に実行することができる。コードが自分自身のコードを調べたり、変更したりする機能はReflectionと呼ばれる。また、クラスファイルは動的にロードされるようなシステムになっているため、JVMを持つweb ブラウザが遠くにあるクラスをロードして実行するといった機能も可能になっている。
今回は、まず、JavaBeansの主なアプリケーションであるJavaのGUIプログラミングについて解説する。そのあとで、JavaBeans技術の元になっているReflectionやSerializationについて述べる。
JavaのGUIプログラミング:AWT
Javaの開発環境であるJDK(Java Development Kit)には、GUIをもつプログラムの作成を容易にするために、Abstract Window Toolkit(AWT)というパッケージが提供されている。AWTは、ボタンやメニューなどのGUI部品、イメージの表示、マウスやキーボードのからの入力(イベント)を処理する機能を提供している。これらの部品は特定のwindowシステムに依存しないようになっている。
AWTが提供しているクラスは、以下のものである。
UI部品:ボタンやメニューなどの部品、それらを配置する入れ物に当たるパネルやウインドウクラス。
配置管理:部品をどのように配置するかを管理するためのLayoutクラス
その他:グラフィックスライブラリ。
ますは、簡単なプログラムをあげて解説する。このプログラムはボタンを2つもち、ひとつのボタンを押すと、helloと表示し、もうひとつのボタンを押すとその文字を消すという簡単なものである。
プログラムは、AWTを使うためにjava.awt.*をインポートする。イベント処理のために、java.awt.event.*をインポートしておく。クラス名は、helloGUIである。
Mainでは、まず、境界線をもつWindowであるFrameを作る。
Frame f = new
Frame(“hello world”);
このFrameは、Containerのサブクラスで、Containerは他のコンポーネントを配置するクラスである。次に、自分のクラスのインスタンスを生成し、それを初期化したあと、このFrameの中にいれる。
helloGUI
hello = new HelloGUI();
hello.init();
hello.start();
f.add(hello,”Center”);
なお、Frameにいれたあとで、サイズを設定し、これを表示させると中にはいっているコンポーネントはすべて表示される。
f.setSize(300,100);
f.show();
アプレットとJavaアプリケーション
このプログラムは、Applet をextendsしており、アプレットとしても使うことができるようになっている。ついでに、アプレットとJavaアプリケーションの違いを述べておこう。
アプレットとは、webページに組み込まれるJavaプログラムである。webページに
<APPLET CODE=”HelloGUI.calss” WIDTH=300 HEIGHT=100>
</APPLET>
と書き、このページと同じところにおいておけば、webブラウザがこのjavaプログラムをネットワークを使ってダウンロードし実行してくれる。アプレットにするためには、java.applet.*をインポートし、appletをextendsして作成する。アプレットクラスはContinerクラスのサブクラスであるPenelクラスをサブクラスとなっており、ブラウザにおいて、このアプレットクラスを生成し、ロードされたアプレット(つまり、HelloGUI)ブラウザのウインドウ(これがFrameにあたる)を表示してくれる。したがって、アプレットとして実行する場合にはFrameは必要ない。実行を開始する前に、アプレットはinitメソッドを実行し、そのあとで、startメソッドを実行する。initには、通常のコンストラクタに記述する内容、すなわち、インスタンス変数の初期化、コンポーネントの設定、などを行う。通常、アプレットとして実行されるときには、すでにアプレットのインスタンスはできているため、このinitで初期化を行う。このほかに、アプレットを一時停止する処理を書くstopメソッドがあり、別のwebページに移ったときに関係するスレッドを停止する処理などを書くことができる。
なお、アプレットとアプリケーションの大きな違いとしてセキュリティがあげられる。任意の他人のブラウザで実行されることを仮定しているために、たとえば、アプレットを実行しているコンピュータでファイルの読み書き、他のプログラムの起動、ロードしたコンピュータ以外への通信などが禁止されている。
コンポーネントのレイアウト
さて、元に戻ることにしよう。初期化initにおいて、まず、レイアウトのための設定する。
setLayout(new BorderLayout());
このBorderLayoutは、コンテナを5つの領域(上、下、右、左、中央)にわけてそのどこかに配置するレイアウトである。これをコンテナに設定するメソッドがsetLayoutで、部品を入れるときには、addを使う。入れる部品について、インスタンス変数を宣言しておき、
Button button1; // 部品のインスタンスの変数を宣言
次に、initにおいて、インスタンスを生成し、
button1 = new
Button(“hello!!!”); // 部品を作る
部品をコンテナにいれる。
add(button1,”East”);
なお、labelは、文字を表示するための部品である。
イベント処理
通常のCの入出力では、getcやreadの処理が呼び出さないと入力は行われない。これに対して、JavaのGUI部品は自律的に動いていると考えることができる。すなわち、マウス操作はキーボード入力を行うと何かの処理が実行される。これをイベントとよび、このイベント処理を記述することがGUIプログラミングの中心的な部分となる。(実際、Javaでは別にスレッドが動いており、それらが入力について監視していると考えることができる)オブジェクト指向言語であるJavaでは、イベント自身もイベント処理もオブジェクト指向のフレームワークで構成されている。
イベントはイベントが発生したところ(event source)から、そのイベントをうけとって処理をするところ(listerner)に伝えられる。リスナーにはイベント処理のメソッドが定義されており、イベントに応じた処理をすることになる。これは、代理人リスナーモデル(delegation-based Listener Model)と呼ばれ、Java1.1で導入されたモデルである。イベントを受け取って、処理をするリスナーの機能を持たせるためにはjava.util.EventListenerインタフェースである<EventType>Listenerインタフェースをインプリメントする。たとえば、ボタンをクリックした時に生成されるActionEventを受け取るのはActionEventListenerインタフェースを実装したクラスで、このリスナークラスのactionPerformedメソッドにボタンを押したときに起こる振る舞いを記述する。
プログラムにおいて、helloGUIクラスにボタンをおした時のActionListenerを実装している。イベントソースであるボタンに対し、そのイベントを伝える相手を指定する部分が、
button1.addActionListener(this)
である。これで、button1に関するイベントが起きたときに、リスナーである、このクラスのインスタンスに伝えられactionPerformedが呼び出されるようになる。ここで引数になっているのが伝えられたイベントactionEventである。このactionEventにはどこから伝えられたイベントなのかという情報が含まれており、actionPerformedではこれをつかって、labelにある文字を消したり、表示したりしている。
if(e.getSource() == botton1) label.setText(“hello”);
イベントには、マウスが移動したり、スクロールバーが移動したりといった様々なイベントがあり、リスナーがある。このプログラムでは、リスナーをこのクラスのオブジェクト自身が受け取っているが、別に設定することも可能である。
このようなGUIプログラミングのようにオブジェクトに対し、イベントが発生し、それを処理するというモデルはJavaBeansでも基本的なモデルになっている。
JavaのReflection機能
Reflectionとは、「反映」「反射」という意味であるが、プログラムが(他の)プログラムを調べるという意味である。Javaのreflection機能を提供するjava.lang.reflectを用いることによって、あるクラス(クラスファイル)にどのようなフィールド(インスタンス変数)、メソッド、Constructorがあるかをしらべたり、オブジェクトのフィールドの値を読み書きしたり、メソッドを適当な変数を与えて呼び出すことができる。
この機能を使うためのサンプルプログラムについて解説する。まず、対象とするプログラムはab.javaというファイルに定義されたabというクラスである。ここにはa,bという変数、aという変数をセットする関数setA,読み出す関数getA、これらを足し算する関数plus、値をプリントする関数printが定義されている。
% javac ab.java
として、コンパイルしておこう。
この内容を調べるのが、report.javaである。reflection機能を使うために、java.lang.reflect.*をインポートしておく。まず、クラスの情報を取得するのがClass.forNameである。
Class cls;
cls = Class.forName(“ab”);
この関数ではクラスがみつからなかったときにexceptionを返すので、catchしておくようにしておかなくてはならない。forNameはClassクラスの静的メソッドとして定義されている。clsにはクラスの情報を表現しているオブジェクトである。フィールド、すなわちインスタンス変数の情報を取得するメソッドが、getDeclaredFieldsである。これは、Fieldsオブジェクトの配列を返す。Fieldsオブジェクトはclsにあるフィールドの情報が入る。これをtoStingメソッドで文字列に変換して、出力する。このプログラムを実行すると、
%java report
Field:
ab.a
ab.b
と表示される。
では、インスタンス変数を読んだり、セットしたりしてみる。このプログラムがtest.javaである。同じように、Class.forNameでクラスの情報を取得しておく。名前を指定して、フィールド情報を取るのがgetFieldである。
Field a = cls.getField(“a”);
constructorの情報を取得する関数が、getConstructorである。このメソッド関数では引数のタイプ情報を与える。タイプ情報は、Classの配列で、primitiveタイプの場合にはデータ型のラップクラス(primitiveをクラスオブジェクトとして扱うためのクラス)に定義されている。
Class pType1[] = { Integer.TYPE, Integer.TYPE};
Constructor Cons = cls.getConstructor(pType1);
メソッド情報を取得する関数がgetMethodで、こちらはメソッド名と引数の情報を与える。
Method setA = cls.getMethod(“setA”, pType2);
上のコンストラクタを呼び出す場合には、ConstructorクラスのnewInstanceメソッドを呼び出す。引数については、すべて、オブジェクトの配列として与える。したがって、primitiveタイプの場合は、ラップクラスを使う。
Object args[] = { new Integer(10), new Integer(20) };
object obj = Cons.newInstance(args);
返されるのも、オブジェクトである。フィールド情報を使って、フィールドを読み出す場合には、getIntをつかう。ここでは、フィールドを「intとして」読み出すことと注意。
a.getInt(obj);
メソッドを呼び出すときには、invokeメソッドを使う。
setA.invoke(obj,arg);
なお、plusメソッドのように値を返すメソッドの場合には、帰り値はObjectタイプになっており、適当なタイプにキャストして、つかわなくてはならない。
次回は、JavaBeansを支えるもうひとつの機能Serializationについて説明し、JavaBeansの説明を行う。