1| public void sub () { 2| Float x, y; 3| 4| y = pop(); 5| x = pop(); 6| push(new Float(x.floatValue() - y.floatValue())); 7| }基本的には、メソッドの名称を add から別のもの(この場合は subにした)に変え、最後に push を呼び出す際の計算を足 し算から他のものに変えるだけである。
一つ注意点があり、加算や乗算のように演算数と被演算数が入れ替わっても結果 が変わらない場合は別として、減算や除算では入れ替わると結果が変わる。その ため、スタック上にどのようにデータが積まれているかを間違えないようにしな ければならない。通常引き算では「x - y」となるがこれを RPN で表 記すると「x y -」となる。そこで、スタックに x, y の順 で push することになるが、それを pop する時には y が先に出てくることになる(LIFO: Last In First Out)。そのため、 上のメソッドでは最初に pop する側を yに入れて変数名か らもわかりやすくなるようにしている。
次の「練習&宿題」については、例えば以下のような main メソッド とすれば良い。なお、ここでは減算が sub(), 乗算が mul(), 除算がdiv() という名称のメソッドだと仮定してい る。
1| public static void main (String args[]) throws Exception { 2| RpnCalc c = new RpnCalc(); 3| BufferedReader stdin = new BufferedReader( 4| new InputStreamReader(System.in)); 5| String a; 6| 7| while (true) { 8| a = stdin.readLine(); 9| if (a.equals("+")) { 10| c.add(); 11| } else if (a.equals("-")) { 12| c.sub(); 13| } else if (a.equals("*")) { 14| c.mul(); 15| } else if (a.equals("/")) { 16| c.div(); 17| } else if (a.equals("p")) { 18| System.out.println(c.value()); 19| } else if (a.equals("q")) { 20| System.exit(0); 21| } else { 22| c.push(new Float(a)); 23| } 24| } 25| }入力文字として、「+」、「-」、「*」、 「/」が来ればそれぞれ加減乗除計算をして、「p」が来ると 最新の計算結果を表示する。また、「q」を入力すると終了する。それ 以外であれば、実数が入力されたと仮定して、その値をスタックに積む。
最後の「練習&宿題 (Advanced)」については、まず入力データが間違って数字 以外であった場合については、以下のように main() メソッド中の new Float(a)(上記22行目) に try 文をかぶせてやれば良い。
1| try { 2| c.push(new Float(a)); 3| } catch (NumberFormatException e) { 4| System.err.println("input error"); 5| }次にコマンド「c」の処理であるが、ともかくひたすらpop を繰り返し、例外 EmptyStackException が起きれば次の入力を求める という風にすれば良い。したがって、コマンド「c」の処理は次のよう になる (抜粋。上記19行目辺りに挿入することを仮定)。
1| } else if (a.equals("c")) { 2| while (true) { 3| try { 4| c.pop(); 5| } catch (EmptyStackException e) { 6| break; 7| } 8| } 9| } else if (a.equals(…while (true) で無限ループとしておき、catch で例外を捕 まえたら、break 文でそのループを抜け出す。(注: 実はこの機能は Stackクラスについて十分調べればもっと簡単に実現できることがわか る。Java の Core APIリ ファレンスマニュアルで探してみよ。)
1| import java.awt.*; 2| import java.awt.event.*; 3| 4| class TestWin extends Frame implements WindowListener { 5| public TestWin (String s) { 6| super(s); 7| addWindowListener(this); 8| } 9| 10| public void windowActivated(WindowEvent e) { 11| } 12| 13| public void windowClosed(WindowEvent e) { 14| } 15| 16| public void windowClosing(WindowEvent e) { 17| } 18| 19| public void windowDeactivated(WindowEvent e) { 20| } 21| 22| public void windowDeiconified(WindowEvent e) { 23| } 24| 25| public void windowIconified(WindowEvent e) { 26| } 27| 28| public void windowOpened(WindowEvent e) { 29| } 30| 31| public static void main (String args[]) { 32| TestWin f = new TestWin("Test Window"); 33| f.add(new Label("This is Label")); 34| f.pack(); 35| f.setLocation(100, 200); 36| f.show(); 37| } 38| }独立したウィンドウの機能はクラス Frame によって定義されているの で、そのサブクラスを作り、ウィンドウを表示するプログラムとする。 「implements WindowListener」というのは、そのウィンドウに対する イベントが発生した時に受けとるために必要なクラス定義の枠組をこのクラスに 取り込む指定である。なお、implementsで指定するのはクラスではな く枠組のみを定めたインタフェースと呼ばれるものである。この結果、クラス TestWinはウィンドウとしての機能とイベントを取り扱う機能の両者を 兼ね備えたクラスとして定義される (注: この方法はあまり一般的ではない。通 常はウィンドウの表示用のクラスとイベントを受け取り、処理するクラスを別に する)。
イベントとは、ウィンドウなどに対してユーザの操作やその他の要因によって非 同期に生じる事象を表すものである (実際には直接操作に対応していなくて、シ ステムの都合で生じるものも含まれる)。
メソッドのうち、「window…」というのはすべてインタフェース WindowListenerで枠組が決められているため、このクラスで定義して おかないとエラーになる。ここでは、とりあえずすべて空文としている(つまり、 何もしない)。
クラス Label はウィンドウ上に文字列のラベルを配置するためのもの である。
また、実際のメソッドの文はサブクラス毎に定義してもらうことを目的とし、メ ソッドパターン (メソッド名、返り値の型、引数パターン) のみを宣言したメソッ ドを「抽象メソッド (abstract method)」と呼ぶ。この宣言にもキーワード 「abstract」が用いられる。なお、抽象メソッドを1以上宣言している クラスは必ず抽象クラスとなる。
宣言されているメソッドが全て抽象メソッドである場合、その定義を(抽象)クラ スではなく、「インタフェース (interface)」として宣言することができる。す なわち、インタフェースとは抽象メソッドしか持たない抽象クラスであるとも言 える。Java は「単一継承」、つまり、あるクラスの直上のスーパクラスが唯一 であるが、このインタフェースを用いることにより、他の言語にある「多重継承」 的な定義を行うことができる。
つまり、あるクラスでは「extends」でスーパクラスを一つしか指定でき ないが、「implements」で複数のインタフェースを指定することがで きる。
Java のプログラムをコンパイルする際、抽象クラスとして宣言されていないク ラス定義において、スーパクラスや指定したインタフェースの抽象メソッドを全 て(具象)メソッドとして再定義していない場合は、エラーとなる。これによって 指定したインタフェースなどで定められた機能を必ずクラスで定義していること を保証するのである。
GridLayout g = new GridLayout(y, x); f.setLayout(g);ここで、y, x はウィンドウ上に並べる要素の縦横の数である。 これで、add()するごとに碁盤目状に並べられる。
クラスを定義する際、スーパクラスは一つしか指定できない(単一継承のため) が、インタフェースはいくつも指定することができる。そこで、前出のクラス TestWin でこの ActionEvent も受けとるようにしてみる。 まずクラス定義の最初を次のように変える(複数のインタフェースを指定する際 は、コンマで区切る)。
class TestWin extends Frame implements WindowListener, ActionListener {次にこのイベントを受けとるためのメソッドactionPerformed() を次のように追加する。
public void actionPerformed (ActionEvent e) { System.err.println(e.getActionCommand()); }最後にmain()メソッドで Buttonインスタンスの生成とリス ナの指定を次のように行う。
1| public static void main (String args[]) { 2| TestWin f = new TestWin("Test Window"); 3| GridLayout g = new GridLayout(2, 1); 4| f.setLayout(g); 5| f.add(new Label("This is Label.")); 6| Button b = new Button("Click Me"); 7| b.addActionListener(f); 8| f.add(b); 9| f.setLocation(100, 200); 10| f.pack(); 11| f.show(); 12| }
なお、ActionEvent のメソッドgetActionCommand()は各ボタ ンに指定されているコマンド文字列(デフォルトではボタンに付けられたラベル) を取り出す。これによって、どのボタンがクリックされたのかを識別することが できる。
getActionCommand() で返される文字列を条件判断に用いて、「終了」 ボタンを追加せよ。まず、Buttonインスタンスとしてラベルに "Quit"とついたボタンを追加する。このボタンにも同様に addActionListener()しておく。そして、actionPerformed() メソッド内で、getActionCommand()した結果の文字列を使って、条件 分岐し、文字列が "Quit" であればプログラムの実行を終了するよう にせよ。