今回のテーマは「メソッド (method) 」です。 Java のメソッドは、プログラムの一部を抜き出し、 ひとまとめにして名前をつけたものです。 その名前を指定することによって、 メソッドの中身として書いてある処理を行うように指示することができます。 これをメソッドの起動 (invoke) 、あるいはメソッドの実行と言います。
(Java のメソッドは C の関数と似た概念のため、 「関数呼び出し (function call) 」という言い方にちなんで、 「メソッドの呼び出し」と言うこともあります。)
次のような表示を行うプログラムについて考えてみましょう。
ポチがないた ワン ワン ワン ワン ワン ワン
public class Pochi1 { public static void main(String[] args) { System.out.println("ポチがないた"); System.out.println("ワン ワン"); System.out.println("ワン ワン"); System.out.println("ワン ワン"); } }
上のプログラムの下線部分に注目してください。 「鳴き声を表示する」という仕事が3回書かれています。 Javaには、プログラム中のまとまった一つの仕事を抜きだし、 わかりやすい名前をつけてまとめておく機能があります。 このまとまりをメソッドと呼びます。 そして、一度メソッドを作っておくと、 メソッドの内容を何度も簡単に実行することができます。
上のプログラムの System.out.println("ワン ワン"); という処理を 1つのメソッドにして barkという名前をつけましょう。 このメソッドbarkを用いると次のように書くことができます。
public class Pochi2 { public static void main(String[] args) { System.out.println("ポチがないた"); bark(); bark(); bark(); } static void bark() { System.out.println("ワン ワン"); } }
mainの中カッコの中身を見てみましょう。 メソッドbarkの処理を実行するには、 bark()と書きます。 このプログラムでは鳴き声は3回表示するわけですから、 メソッドを3回連続して実行すれば良いことになります。
プログラムの後半のbarkの中カッコの中身には、 中で行う処理の具体的な内容を書いておきます。 これを「メソッドの宣言」と言います。
詳しい文法は後回しにして、 メソッドを用いるとどんなメリットがあるかをまとめておきます。
この例題では、メソッドを使うことによって、 同じプログラムを複数回書くという手間を省くことができました。 処理の方法に名前をつけて定義しておけば、あとはそれを何回でも実行できます。
また、大きなプログラムをひとまとめに書くと理解するのが大変です。 メソッドによって大きな仕事を部分に分け、それぞれを別々に理解することによって、 プログラムを分かりやすくすることができます。
メソッドに指示を与えるために、 数値や文字列などの情報を与えることができます。
次の例題はメソッドに鳴く回数を指定して、 指定された回数ぶん「ワン」と表示させるプログラムです。
public class Pochi2 { public static void main(String[] args) { System.out.println("ポチがないた"); bark(1); bark(2); bark(3); } static void bark(int number) { for(int i = 0; i < number; i++) System.out.print("ワン "); System.out.println(); } }
実行例:
ポチがないた ワン ワン ワン ワン ワン ワン
メソッドmainの、 メソッドbarkの実行部分に注目しましょう。
bark(1)
メソッドbarkは、 カッコ内に指定された回数ぶん「ワン 」表示するメソッドとします。 このように、メソッドに対して、 メソッドを実行する側から数値や文字などのデータを送ることができます。 このようなデータを「引数 (ひきすう, argument)」と呼びます。
一方、メソッドbarkの宣言部分に注目します。
static void bark(int number) {
int number は、 このメソッドはint型の値1つを受け取って仕事をするものだ、 ということを意味しています。
number はint型の値を入れることができる変数の名前です。 普通の変数と違い、このメソッドの実行を開始したとき、 実行元 (この例題ではmain側) で引数に指定された値が あらかじめ入っていることになります。 したがって、numberの値ぶん「ワン 」と表示すれば、 目的の仕事が達成できるというわけです。
実は、今までのプログラムに必ず現れた main もメソッドの一つです。 一般にプログラムは、次のようにいくつかのメソッドを並べた形をしています。 メソッド一つ一つの宣言は、 main と同列に並べて書けば良いのです。
public class プログラムの名前 { public static void main(String[] args) { .... main メソッド プログラムは main メソッドから実行が開始される .... } メソッドの宣言1 .... メソッドの宣言2 .... .... メソッドの宣言n }
そして、「メソッドの宣言」の形式は次のとおりです。
static void メソッド名 ( 引数の宣言 ) { .... メソッドの本体 .... }
Java はオブジェクト指向プログラミング言語と呼ばれていますが、 まだオブジェクトのことは学んでいません。 static はオブジェクトを使わないプログラムにおいて、 メソッドを使うためのキーワードです。 今のところ「おまじない」としておいて構いません。 逆に、次々回以降に学ぶオブジェクトを使ったプログラムでは、 基本的に static をつけてはいけません。
voidは、 いまのところはおまじないとしておきます。 詳しくは次回にまわしますが、 メソッドは、単に処理を行うだけではなく、 計算を行った結果を相手に返すこともできるのです。 ここには本来、メソッドがどんな型の値を返すのかを設定します。
「引数の宣言」の部分には、引数の型と変数名を並べて書きます。 書き方は普通の変数宣言とほぼ同様ですが、 セミコロンは書きません。引数が複数個ある場合は、コンマで区切ります。 また、引数のないメソッドの場合、カッコの中を空欄にします (カッコ自体は省略できません)。
同じ型の引数であっても int a, b のような書き方は間違いです。 引数ひとつごとに int a, int b のようにし、 別々に型を書かなければいけません。
メソッド宣言の最初の一行の例をいくつか示しておきます。
static void result(int a) { ... static void compute(int subject, int times) { ... static void getInfo() { ... static void measure(double x, int t) { ... static void summarize(int[] a, int value) { ...
上の最後の例のように、引数に配列を渡したい場合、 「引数の宣言」の部分に配列を示す型 (int[] など) を書き、 続けて配列名を書きます。
メソッドの本体の書き方はこれまでのプログラムと同じです。 通常の変数宣言や、文を並べて書けば上から順に実行されることになります。 また、メソッドは宣言された順序や場所に関わらず、 実行元から呼び出されたときにはじめて実行されることに注意してください。
例えば、次のようなプログラムがあったとします。
public class AClass { static void process() { .... } static void compute() { .... } static void submit() { .... } public static void main(String[] args) { compute(); process(); } }
このプログラムでは、最初にメソッドprocessの宣言があり、 順にcompute, submit, mainの宣言がありますが、 実際に一番最初に実行されるのはmainです。 Javaではmainが最初に実行されるという決まりがあります。 次に、mainの中でメソッドcomputeを実行しています。 この時点でcomputeの中身の処理が行われます。 そして次に、processの処理が行われます。 一方、メソッドsubmitは、どのメソッドからも実行されていません。 この場合、submitの処理は全く行われません。 メソッドの中身の処理を行うためには、 どこかから実行される必要があるのです。
プログラムの中でのメソッドの実行順序は、 mainを起点とし そのメソッドが実行元から実行された順です。 テキスト形式でプログラムを書いた順番ではありません。
引数 (ひきすう) は、 メソッドの実行元からメソッド内へ値を引き渡す手段です。
次のプログラムを見てください。
public class TriangleWithMethod2 { public static void main(String[] args) { for(int i = 1; i <= 4; i++) printAsterisk(i); } static void printAsterisk(int number) { for(int i = 0; i < number; i++) System.out.print("*"); System.out.println(); } }
メソッドの中から見た引数、 例えば printAsterisk のメソッド宣言における変数 number を仮引数 (かりひきすう) と呼びます。 一方、実行元から引数で渡すもの、 例えば main の中の printAsterisk に渡している i のことを実引数 (じつひきすう) と呼びます。
メソッドの中の仮引数は、実行元から指定された実引数によって 初期値が決まっているというだけで、 普通の変数と同様に使うことができます。
整数と実数の場合、 引数は実行元からメソッド内へ一方通行で値を渡すことだけができます。 一度値が渡された後は、それぞれは別の変数として使うことになります。 一方で値を変更しても、その影響が他方に及ぶことはありません。
たとえば、上の例の printAsteriskは次のように書いても構いません。
static void printAsterisk(int number) { while(number > 0) { System.out.print("*"); number--; } System.out.println(); }
メソッド printAsterisk では、 仮引数である変数 number の値を変化させながら処理を行っていますが、 メソッドの実行元である メソッドmainの i には影響はありません。
ただし、この規則には例外があります。 配列や文字列などを引数とした場合、 メソッド内で仮引数の値を変えると、 実行元から見た実引数の値も変化してしまいます。 このように、 引数を使った値の受渡しについては、少し変則的な決まりがあります。 詳しい説明は後まわしにしますが、頭の隅に入れておいてください。
メソッドを用いたプログラムの中には様々な変数が登場することになります。 しかし、プログラムのどこからでも変数が使えるわけではありません。 変数の使える有効範囲のことをスコープと呼びましたね。
変数は、宣言された最も近い中カッコの中でのみ使うことができる、 という規則を思い出してください。
public class TriangleWithMethod3 { public static void main(String[] args) { int i; for(i = 1; i <= 4; i++) printAsterisk(i); } static void printAsterisk(int number) { int i; for(i = 0; i < number; i++) System.out.print("*"); System.out.println(); } }
この例では、メソッドmainの中 (中カッコの内側) で、 宣言された変数 i は、 mainの中でのみ有効な変数です。 一方、メソッドprintAsteriskにも同じ名前の変数 i が 使われています。 これらは、名前は同じでも有効範囲が異なるため、全く違う変数として使われます。 main側でiの値を変更しても、 printAsterisk側ではiの値に影響はありません。
また、printAsterisk中の変数numberは、 このメソッド内でのみ有効な変数となります。
次のような三角形を画面に表示するプログラムを作成します。
*..... **.... *.*... *..*.. *...*. ******
三角形をアステリスク (*) で表示し、 それ以外の背景をピリオド (.) で埋めつくしています。 このような出力をするにはアステリスクやピリオドを複数個連続して 出力するという作業を何回も行う必要があります。 この部分をメソッドとして独立させれば良いということになります。
public class TriangleWithMethod3 { public static void main(String[] args) { int size = 5; System.out.print("*"); repeatCharacters(size, "."); System.out.println(); for (int i = 1; i <= size - 1; i++) { System.out.print("*"); repeatCharacters(i - 1, "."); System.out.print("*"); repeatCharacters(size - i, "."); System.out.println(); } repeatCharacters(size + 1, "*"); System.out.println(); } static void repeatCharacters(int count, String charactor) { for (int i = 0; i < count; i++) System.out.print(charactor); } }
配列を用いたプログラムでは、 配列要素をすべて表示するような処理が頻繁に使われます。 これをメソッドとして独立させることを考えてみます。
このためには、メソッドに対してどの配列の内容を表示するかを 引数を用いて渡す必要があります。 メソッドへは、個別の値ではなく配列そのものを渡すことができるのです。 配列を引数に用いて内容を表示するメソッドは次のように書くことができます。
static void print(int[] a) { for(int i = 0; i < a.length; i++) System.out.print(a[i] + " "); System.out.println(); }
一方、このメソッドを実行する側では、 次のように引数に配列の変数名のみを書きます。 ここでの変数名とは、今までの配列の使い方から カッコや添字などを省いたものであることに注意してください。
int[] a = {15, 9, 12, 10, 6, 8, 16, 5, 13, 14}; print(a);
このメソッドを使ったプログラム全体を示します。
public class ArrayPrinter { public static void main(String[] args) { int a[] = {15, 9, 12, 10, 6, 8, 16, 5, 13, 14}; print(a); // a[0] を 2 に置き換える a[0] = 2; print(a); } static void print(int[] a) { for(int i = 0; i < a.length; i++) System.out.print(a[i] + " "); System.out.println(); } }