Javaのメソッドの定義や意味、効果はオブジェクト指向に大きく関わっているが、 このページでは「オブジェクト指向抜き」でメソッドを説明している。 したがってこのページの説明には正確さを欠くところや不十分なところがある。 これは、いきなりオブジェクト指向としてのメソッドを習得することは敷居が高いと考えるからである。
このページの内容はオブジェクト指向言語以前の手続き型言語(CやBasicなど)でも通用する内容であるため、 適用範囲が広い。ぜひ習得して欲しい。
Javaでいうメソッドとは、一連の処理を一つにまとめたものである。 例えば、System.out.println は、Javaの通常の環境で最初から用意されているメソッドであり、 実際には数十行のプログラムが組み合わされて作られている。 もし、System.out.printlnメソッドがなければ、 画面に表示するたびに数十行のプログラムを自分で書かなければならなくなる。
従来の手続き型言語(CやBasicなど)でメソッドに相当するものは、手続き(procedure)、 関数(function)、サブルーチン(sub routine)などと呼ぶ
例えば以下のプログラムがある。
public class no_use_methods { public static void main(String[] args) { int i; i = 0; if (i < 0) { System.out.println("エラー:変数の値が不正です。"); } i++; if (i < 0) { System.out.println("エラー:変数の値が不正です。"); } i-=10; if (i < 0) { System.out.println("エラー:変数の値が不正です。"); } i=100; if (i < 0) { System.out.println("エラー:変数の値が不正です。"); } } }このプログラムでは、変数iが負の値(i<0)であれば、不正な値としてエラー表示をしている。 このエラー表示の処理を、例えばerror_checkメソッドとして定義すれば、 以下のようにプログラムが書きかえられる。
public class use_methods { public static void main(String[] args) { int i; i = 0; error_check(i); i++; error_check(i); i-=10; error_check(i); i=100; error_check(i); } static void error_check(int i) { if (i < 0) { System.out.println("エラー:変数の値が不正です。"); } } }
error_checkメソッドを定義したことで、毎回if文があった場所が、error_check(i); だけになり、プログラムがすっきりと見やすくなったことに注目して欲しい。
このように、プログラムの中で何度も行っているような処理をメソッドとして定義することで、 プログラムを見通しよく、読みやすく書くことができる。
以上の説明だけを読むと、字面が似通った文が何度も出てくるところをメソッドとして定義するように聞こえるが、 どちらかというと、意味として同一のもの、機能としてまとまるものをメソッドとするほうが良い。
例えば、同じようなメッセージの表示が出てくるところをまとめるというよりも、 「配列に格納されているデータを表示する」、「エラーの場合のメッセージを表示する」、 といったほうが望ましい。何をメソッドにするかは、一種のセンスの問題ともいえ、 ある程度長い期間プログラミングをしないとなかなか身につかないものである。 プログラムを作るときは、意味としてまとまっているところがないか常に考える習慣をつけて欲しい。
なお、今まで書いてきたpublic static void main(String[] args)もメソッドの一つであり、 mainメソッドと呼ぶ。 mainメソッドはJavaアプリケーションで実行時に最初に実行されるメソッドである。 逆にいうと、mainメソッドがないとJavaアプリケーションを実行しても何も実行されない (実際にはmainメソッドがないという実行時エラーになる)。
メソッドは、プログラム中のある場所から呼び出すことで実行できる。 例えば、上記のプログラムでは、mainメソッドの中から4回 error_checkメソッドを呼び出している。 呼び出すと、error_check メソッドの本体( if (i < 0) { , System.out.println("エラー:変数の値が不正です。"); , } という3行分)が実行される。 メソッドの本体の実行が終われば、元に戻って続きが実行される。 以下に詳しく説明を添えたプログラムを示す。
1: public class use_methods { 2: public static void main(String[] args) { 3: int i; 4: 5: i = 0; 6: error_check(i); // error_checkメソッドを呼び出す 7: 8: i++; 9: error_check(i); // error_checkメソッドを呼び出す 10: 11: i-=10; 12: error_check(i); // error_checkメソッドを呼び出す 13: 14: i=100; 15: error_check(i); // error_checkメソッドを呼び出す 16: } 17: public static void error_check(int i) { // error_checkメソッドはここから 18: if (i < 0) { // error_checkメソッド本体(3〜5行目) 19: System.out.println("エラー:変数の値が不正です。"); 20: } 21: } // error_checkメソッドはここまで 22: }
以上のプログラムの動作を順に追っていこう。 まずプログラムはmainメソッドの先頭から実行を始める。 つまり、3行目、4行目、5行目と実行され、6行目でerror_checkメソッドが呼び出される。 そうするとerror_checkメソッドの本体(18〜20行目)が実行される。 20行目が終わると、元の場所(6行目)に戻ってきて、続きを実行する。 つまり、7行目に移る。
そして、8行目を実行し、9行目で再びerror_checkメソッドが呼び出される。 また、18〜20行目が実行され、9行目に戻ってきて、続きの10行目を実行、 以下、同様で、12行目、15行目でもerror_checkメソッドが呼び出される。 21行目に差し掛かるとmainメソッドが終了するため、プログラム全体が終了する。
例えば、5〜8行目の辺りからerror_checkメソッドが呼び出させる様子は以上の図のようになる。
public class args_example { public static void main(String[] args) { int i = 100, j = 200; String ok = "Okay"; method0(); // 引数のないメソッドを呼び出す method1(i); // 1つの引数があるメソッドを呼び出す method2(i, j); // 2つの引数があるメソッドを呼び出す method3(i, ok); // 2つの引数があるメソッドを呼び出す } static void method0() { // 引数のないメソッド System.out.println("method0が呼ばれました"); } static void method1(int v) { // 1つの引数があるメソッド System.out.println("method1の引数の値は" + v + "です"); } static void method2(int a, int b) { // 2つの引数があるメソッド System.out.println("method2の引数の値は" + a + "と" + b + "です"); } static void method3(int a, String s) { // 2つの引数があるメソッド System.out.println("method3の引数の値は" + a + "と" + s + "です"); } }
引数には型があることに注目して欲しい。 例えば、上記のmethod3は1つめの引数a がint型、2つめの引数sがString型である。 メソッドを呼び出す場合、引数に定義と異なる型のデータを渡すことはできない (ただし暗黙の型変換がなされる場合を除く)。 例えば、上記のmainメソッドの中で method2(s,j); や method3(s, i); method1(s); というような呼び出しはエラーとなる。 また、当然引数の個数が違う場合もエラーとなる(例: 上記で method0(i); と書くとエラー)
上記のプログラムで、mainメソッドの中では i, j, ok という名前の変数を使っているのに、 method0〜3の引数には、v, a, b, s といった名前がついている。
メソッドを呼び出す時は、引数としてデータを渡すことができるが、 呼び出すほうの式を実引数、呼び出されたほうを仮引数と呼ぶ。 上記の場合、i, j, okが実引数、v, a, b, sが仮引数である。
引数を渡す場合、原始型(int, floatなど)の場合は、実引数の値が仮引数に渡される。 つまり、例えば、上記のmainメソッド内で method1(i); と呼び出されると、 実引数iの値、つまり100がmethod1の仮引数vに渡され(代入され)、 method1の本体(System.out.println("method0が呼び出されました");)が実行される。
値が渡されるだけなので、実引数である変数の値は変化しない。例えば、 以下のプログラムでi の値は何も変わらない。public static void main(String[] args) { int i = 10; plus(i); System.out.println("i=" + i); // 実引数に変化なし } static void plus(int a) { a = a + 20; System.out.println("a=" + a); // 当然aは20増える }
なお、いままで敢えて実引数と仮引数の名前を別にしていたが、同じにしても問題はない。 例えば、上記のプログラムは以下のように書いても差し支えない。
public static void main(String[] args) { int i = 10; plus(i); System.out.println("i=" + i); // 実引数に変化なし } static void plus(int i) { i = i + 20; System.out.println("i=" + i); // mainメソッド内の変数iとは別物 }
mainメソッド内の変数iとplusメソッド内の仮引数iは名前は同じだが全く別物であることに注意せよ。 2つの整数値を入れられる箱があり、どちらにもiという名前がついていて、 一方はmainメソッド内から使え、他方はplusメソッドから使えるようになっていると考えればよい。
参照型(String型や配列など)の場合は、参照が渡される。この場合、実引数と仮引数は同じものを指しているので、 仮引数の内容を変化させると実引数の内容も変わってしまう。以下の例を見よ。
public static void main(String[] args) { int[] a = {100, 200, 300}; plus(a); // 参照型である配列を渡す System.out.println("配列a:" + a[0] + "," + a[1] + "," + a[2]); } static void plus(int[] b) { for (int i = 0; i < b.length; i++) { b[i] += 50; } System.out.println("配列b:" + b[0] + "," + b[1] + "," + b[2]); }
メソッドには返り値(戻り値とも言う)を設けることができる。引数がメソッドへの入力とすると、 返り値はメソッドからの出力といえる。Javaの場合、メソッドの引数は複数個指定できるが、返り値は1つしか指定できない。あるいは、返り値なしとなる。 今まで挙げたメソッドは全て返り値なし(void)である。
Javaで返り値を指定するには、返り値を書く。今まで void と書いていた個所である。 返り値には引数のような名前は付かない。 例えば、返り値がintであるメソッド total は以下のように書く。
static int total() { return 100; }
返り値のあるメソッドには必ずreturn文を書かなければならない。上記の例では、 return 100; で返り値として100が返る。 このtotalメソッドを呼び出す側は以下のようになる。
int i; i = total(); System.out.println(i);
この場合、totalメソッドの返り値100が変数iに代入され、iは100になる。 なお、return文に差し掛かるとそのメソッドはそこで終了となる。 また、返り値のないメソッドの場合のreturn文は、単にreturn; と書けばよい。
static int signal(int a) { if (a < 0) { return 0; } for (int i = 0; i < a; i++) { System.out.print("赤黄青"); } return a; }
上記のプログラムの場合、signalの引数が負の値の場合、return 0; が実行され、 signalメソッドはそこで終了、呼び出された場所に戻って、処理が再開される。
public class kadai1 { public static void main(String[] args) { System.out.println("まいどありがとうございます"); System.out.println("中華料理店北京です"); System.out.println("只今準備中です"); System.out.println("まいどありがとうございます"); System.out.println("中華料理店北京です"); System.out.println("只今ランチメニューがございます"); System.out.println("まいどありがとうございます"); System.out.println("中華料理店北京です"); System.out.println("本日休業日です"); } }
以下のプログラムのmainメソッド中からのmethod0〜4の呼び出しは全てエラーとなる。 それぞれの呼び出しについてなぜエラーになるのか説明せよ。
public class args_example { public static void main(String[] args) { int i = 300, j = 50; String s = "No!"; method0(i); method1(s); method2(i, s); method3(j, i); method1(i, j); method4(j); } static void method0() { System.out.println("method0"); } static void method1(int v) { System.out.println(v); } static void method2(int a, int b) { System.out.println(a + b); } static void method3(int a, String s) { System.out.println(a + s); } static void method4(String s) { System.out.println(s); } }
public class triple_array { public static void main(String[] args) { int[] a = {1, 5, 2, 9, 5}; int[] b = a; for (int i = 0; i < b.length; i++) { b[i] = b[i] * 3; } System.out.println("配列bの内容"); disp_array(b); System.out.println("配列aの内容"); disp_array(a); } static void disp_array(int[] a) { int i; for (i = 0; i < a.length-1; i++) { System.out.print(a[i] + ","); } System.out.println(a[i]); } }
public class triple_array { public static void main(String[] args) { int[] a = {1, 5, 2, 9, 5}; int[] b = new int[a.length]; for (int i = 0; i < a.length; i++) { b[i] = a[i] * 3; } System.out.println("配列bの内容"); disp_array(b); System.out.println("配列aの内容"); disp_array(a); } static void disp_array(int[] a) { int i; for (i = 0; i < a.length-1; i++) { System.out.print(a[i] + ","); } System.out.println(a[i]); } }