次のプログラムを考えてみましょう。
import java.io.*; class Test { public static void main(String args[]) throws IOException { DataInputStream d = new DataInputStream(System.in); // データ入力の準備 System.out.print("あなたは何歳ですか? "); System.out.flush(); // 強制出力 String s = d.readLine(); // 文字列の入力 int a = Integer.parseInt(s); // 整数に変換 System.out.print("初恋は何歳でしたか? "); System.out.flush(); // 強制出力 String s = d.readLine(); // 文字列の入力 int b = Integer.parseInt(s); // 整数に変換 System.out.println("あれから " + (a - b) + " 年たちましたね"); } }
これは完璧なプログラムですが, メッセージを出力して整数を入力する部分が繰り返し現れています。 同じようなところは,まとめて「メソッド」として定義するのが賢い方法です。 メソッドを使ってまとめると次のようになります。
import java.io.*; class Test { static int getInt(String s) throws IOException { DataInputStream d = new DataInputStream(System.in); // データ入力の準備 System.out.print(s); // メッセージ出力 System.out.flush(); // 強制出力 String t = d.readLine(); // 文字列の入力 return Integer.parseInt(t); // 整数に変換して返す } public static void main(String args[]) throws IOException { int a = getInt("あなたは何歳ですか? "); int b = getInt("初恋は何歳でしたか? "); System.out.println("あれから " + (a - b) + " 年たちましたね"); } }
メッセージを出力し,整数を入力する部分を,getInt と いうメソッドにまとめました。
じつは main も一つのメソッドです。 上のプログラムは,main と getInt の二つのメソッドを 含むことになります。
メソッド getInt の頭に付いている static は, main にも付いている語ですね。この意味は,後で詳しく述べます。
その後の int は,getInt が int 型の値 を返すことを意味します。 これに対して,main メソッドに付いている void という語は, main メソッドが何も値を返さないことを意味します。
getInt(String s) の String s は, この getInt というメソッドが, 文字列(ストリング)を受け取ることを意味します。 受け取った文字列は s という変数に代入されます。
この文字列 s のようにメソッドが受け取るもののことを, メソッドの引数(ひきすう)といいます。
メソッドを使うことを,メソッドを「呼び出す」ともいいます。 ここでは
int a = getInt("あなたは何歳ですか? ");のように,
"あなたは何歳ですか? "
という文字列を
引数としてメソッド getInt() を呼び出しています。
このメソッドは,キーボードからの入力を整数に直して,
その値を返します。
メソッドが返す値のことを戻り値(もどりち)などといいます。
この getInt() メソッドの中では, キーボードから入力する部分がありますが, ここでは入出力の例外(I/O Exception)が生じる可能性があります。 例外とは,いわゆるエラーのことです。 エラーが生じたときは,自前でエラー処理することもできますが, エラー処理を他に任せてしまうこともできます。 このための宣言が throws IOException でした。
throws IOException は,このメソッドの中で入出力エラーが 生じたなら,それをそのまま呼び出し側に投げる(throw する)ことを 意味します。つまり,main メソッドから getInt メソッド を呼び出したとき, getInt メソッド内で生じた入出力エラーは main メソッド にそのまま投げられるので,main メソッドもまた 入出力エラーを投げる必要があります。最後に入出力エラーを受け取って エラーメッセージを発するのは,Javaインタープリタです。
エラーを投げないで,メソッド内で自前で処理してしまうことも可能です。 それには,次のようにします。
import java.io.*; class Test { static int getInt(String s) { DataInputStream d = new DataInputStream(System.in); // データ入力の準備 System.out.print(s); // メッセージ出力 System.out.flush(); // 強制出力 try { // トライする return Integer.parseInt(d.readLine()); } catch (IOException e) { // 入出力の例外なら return 0; // 0 を返す } } public static void main(String args[]) { int a = getInt("あなたは何歳ですか? "); int b = getInt("初恋は何歳でしたか? "); System.out.println("あれから " + (a - b) + " 年たちましたね"); } }
readLine() のような入出力メソッドを用いるときは, 必ず throws か catch を使わないと, コンパイラに叱られます。
throws でエラーを他に転嫁せず, catch でエラーを捕まえれば,エラーの連鎖は断ち切れ, 他でエラー処理する必要はなくなります。
実際には入出力エラーより, Integer.parseInt() メソッドで 文字列が整数に変換できないというエラーが 起きることのほうが多いと思います。 入出力エラーのように実際に入力装置にエラーが発生したときのエラーは 絶対に throws か catch で処理しなければなりません。 これに対して,文字列が整数に直せないといったような, いわゆる「実行時のエラー」は, throws や catch で処理する必要はありませんが, 処理してもかまいません。 処理するには次のようにします。
try { // トライする return Integer.parseInt(d.readLine()); } catch (IOException e) { // 入出力の例外なら return 0; // 0 を返す } catch (NumberFormatException e) { // 数値形式の例外なら return 0; // 0 を返す }これで,数の形式が変なときも 0 を返すようになります。
さきほどの数値入力の例で,今度はメソッドだけでなく, クラスを独立させてしまいましょう。
それから,ついでですので整数入力ではなく, より一般性のある実数(double 型)の入力にしてみました。
import java.io.*; class GetNumber { DataInputStream d; GetNumber() { // GetNumber クラスを初期化するメソッド d = new DataInputStream(System.in); // データ入力の準備 } double get(String s) { // get というメソッドを作る System.out.print(s); // メッセージ出力 System.out.flush(); // 強制出力 try { // トライする return Double.valueOf(d.readLine()).doubleValue(); } catch (IOException e) { // 入出力の例外なら return Double.NaN; // NaN を返す } catch (NumberFormatException e) { // 数値形式の例外なら return Double.NaN; // NaN を返す } } } class Test { public static void main(String args[]) { GetNumber g = new GetNumber(); // GetNumber クラスを使う double a = g.get("あなたは何歳ですか? "); double b = g.get("初恋は何歳でしたか? "); System.out.println("あれから " + (a - b) + " 年たちましたね"); } }
これ全体を Test.java というファイルに書き込んで, コンパイルすると,Test.class だけでなく, GetNumber.class というクラスファイルも カレントディレクトリにできます。実行するには,以前と同様に,
java Testとします。
このプログラムの get() メソッドは, エラーが起こったときに Double.NaN という変な値を返しています。 この NaN は Not-a-Number(数でないもの)の意味で, double 型や float 型に用意されている特別の値です。
もし double 型でなく int 型が欲しい場合は, 次のようにして,(int) を付けて型変換します。
class Test { public static void main(String args[]) { GetNumber g = new GetNumber(); // GetNumber クラスを使う int a = (int) g.get("あなたは何歳ですか? "); // 型変換 int b = (int) g.get("初恋は何歳でしたか? "); // 型変換 System.out.println("あれから " + (a - b) + " 年たちましたね"); } }
この (int) のように, 型の名前をかっこに入れて付けて型を変換することを, キャストする(cast)といいます。