つまり、自分がプログラムしようとしている問題に応じて、既に存在するもの が利用できるのであれば、できる限り利用する。 つまり、既存のクラスが一種の部品であり、それらを 組み合わせて、足りないものを補うのがプログラミングということになる。
この考え方は他のプログラミング言語などでもある程度当然であるが、 Java などのオブジェクト指向プログラミング言語ではより実践しやすくなっている。
例えば、既存のあるクラスのほとんどの機能はそのまま使えるが一部だけ違うものが 欲しいとする。その時、継承 (インヘリタンス) を用いてサブクラスを定義し、 その「違い」の部分だけをサブクラスで定義するようなことができる。 (前回の Human と Student の例などを考えてみよ。)
逆ポーランド方式の電卓の実物としてはヒューレットパッカード社の電卓や、 X-Window システムのxcalc (残念ながら uni には載っていないが、avalon にはある) で -rpn を指定したもの、それからコマンドプロンプトのところで使える dcなどがある。
1| // RpnCalc.java 2| import java.util.*; 3| 4| class RpnCalc { 5| Stack stk; 6| 7| public RpnCalc () { 8| stk = new Stack(); 9| } 10| 11| public void push (Float n) { 12| stk.push(n); 13| } 14| 15| public Float pop () { 16| return ((Float)stk.pop()); 17| } 18| 19| public Float value () { 20| return ((Float)stk.peek()); 21| } 22| 23| public void add () { 24| Float x, y; 25| 26| y = pop(); 27| x = pop(); 28| push(new Float(x.floatValue() + y.floatValue())); 29| } 30| 31| public static void main (String args[]) { 32| RpnCalc c = new RpnCalc(); 33| 34| c.push(new Float(1.2)); 35| c.push(new Float(3.4)); 36| c.add(); 37| System.out.println(c.value()); 38| System.exit(0); 39| } 40| }逆ポーランド方式の計算ではスタックが必要となるが、これには既存の Stack クラスを利用する。Stack クラスは java.utilにある。
Stack クラスではスタックに積めるものはリファレンス型 (参照型) に限定されている。 (実際には Object クラスあるいはそのサブクラスのインスタンスに 限られている。) そのため、実数を表す基本型 (float) のデータを そのままでは入れられないので、 クラス Float を用いる。Float は基本型 float に対応するリファレンス型の クラスである。そこには float のデータが一個入るようになっている。
また、Stack クラスのメソッド pop() では Object クラスのインスタンスを返すような 定義になっているので、実際に格納されている Float クラスとして 扱うためには型のキャスト (変換) が必要となる。 (メソッド、pop(), value() の定義参照。)
標準出力 (System.out) の方は println() という何でも
データなら表示してくれる
便利なメソッドがあったが、標準入力 (System.in) の方は、
そのままではバイト単位の入力しかできず、例えば、1行入力はできない。
Java では「…Stream」というクラスはバイト単位の入出力を行うものであり、
文字単位の入出力には「…Reader」または「…Writer」を用いる。
(注: System.outの型は旧版との互換性のため、PrintStream
になっているが、本来は PrintWriter であるべきである。)
特に行単位の入力の際には InputStreamReader と
BufferedReader を組み合わせて
使用することになっている。前述のプログラムで計算する2つの数値を標準入力
から入れるようにするには main() メソッドを次のように変える。
この際、
import java.io.*;
が必要になる。
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| a = stdin.readLine(); 8| c.push(new Float(a)); 9| a = stdin.readLine(); 10| c.push(new Float(a)); 11| c.add(); 12| System.out.println(c.value()); 13| System.exit(0); 14| }BufferedReader の readLine() が1行読み込みのための メソッドである。 そして、Float クラスのコンストラクタは文字列が引数の場合はその文字列が 実数を表しているものとして変換してくれる。 ここで、その文字列が数値でなかったりするとエラーとなる。 (そのため、throws Exception が書かれている。)
また、while 文を用いて1回だけの計算で終らずに繰り返すようにしてみよ。 UNIX の dc コマンドのような使い方ができるとなお良い。
なお、if 文、while 文は C 言語とほぼ同様の書き方である。 if は前回の宿題の プログラム例を参考にせよ。while は以下のように記述する。
while (式) { 文 … }ここで、「式」はboolean型を持つ、すなわち真偽を表すものである。
そこで、実行時のエラー、特に「例外」が起きた時にそれをとらえて必要な 処理をする構文がある。try {…} catch (…) {…} というものである。 例えば、Float クラスのコンストラクタで文字列が数字を表さない時に 起きる例外は NumberFormatException というものである。これが起きた時に それを捕まえてエラーメッセージを表示し、続きの処理に移るには、
… try { …new Float(a)…; } catch (NumberFormatException e) { System.err.println("input error"); } …という風に書く。
ここではまず、try の1番目のブロックの処理を実行し、何事もなければ この構文の次の文に進む。 もし、1番目のブロックの処理の最中に例外が起きれば、catch の後に指定したパラメータと例外の型 (クラス) を比較し、一致あるいは そのサブクラスに なっていれば、例外を表すオブジェクトをパラメータに入れて、2番目のブロックを 実行する。もし、クラスが合わなければより外側の例外処理へと移る。
次に計算をどんどん進めていくとスタックに値がどんどん溜っていくことが 考えられる。コマンド「c」が入力されれば、スタックを空にするような 機能を付け加えて見よ。なお、Stack クラスでスタックが空になっても pop しようとすると 例外「EmptyStackException」が発生するので、それをとらえるまで データを pop すれば良い。(もちろん、他の手法でも良い。)