コンピュータ基礎および演習II

講義資料

メソッド

今回のテーマは「メソッド (method) 」です。 Java のメソッドは、プログラムの一部を抜き出し、 ひとまとめにして名前をつけたものです。 その名前を指定することによって、 メソッドの中身として書いてある処理を行うように指示することができます。 これをメソッドの起動 (invoke) 、あるいはメソッドの実行と言います。

(Java のメソッドは C の関数と似た概念のため、 「関数呼び出し (function call) 」という言い方にちなんで、 「メソッドの呼び出し」と言うこともあります。)

メソッドを用いたプログラムの例

次のような表示を行うプログラムについて考えてみましょう。

ポチがないた
ワン ワン クン クン キャン キャン
ワン ワン クン クン キャン キャン
ワン ワン クン クン キャン キャン
class Pochi1 {
    public static void main(String[] args) {
        System.out.println("ポチがないた");

        System.out.print("ワン ワン ");
        System.out.print("クン クン ");
        System.out.print("キャン キャン");
        System.out.println();

        System.out.print("ワン ワン ");
        System.out.print("クン クン ");
        System.out.print("キャン キャン");
        System.out.println();

        System.out.print("ワン ワン ");
        System.out.print("クン クン ");
        System.out.print("キャン キャン");
        System.out.println();
    }
}

上のプログラムの下線部分に注目してください。 「鳴き声を表示する」という仕事が3回書かれています。 このような、プログラム中のまとまった一つの仕事を抜きだし、 メソッドとしてひとまとめにして書いておくことができます。 そして、メソッドの内容を何度も実行することができます。

上のプログラムは、メソッドを用いると次のように書くことができます。

class Pochi2 {
    public static void main(String[] args) {
        System.out.println("ポチがないた");

        berk();
        berk();
        berk();
    }

    static void berk() {
        System.out.print("ワン ワン ");
        System.out.print("クン クン ");
        System.out.print("キャン キャン");
        System.out.println();
    }
}

メソッドmainを見てみましょう。 「鳴き声を表示する」という仕事をメソッドとして独立することにします。 メソッドの名前をberkとしましょう。 メソッドberkの処理を実行するには、 berk()と書きます。 このプログラムでは鳴き声は3回表示するわけですから、 メソッドを3回連続して実行すれば良いことになります。

プログラムの後半部分は、 メソッドberkの中で行う処理の具体的な内容を示しています。 この部分を「メソッドの宣言」と呼びます。

詳しい文法は後回しにして、 メソッドを用いるとどんなメリットがあるかをまとめておきます。

この例題では、メソッドを使うことによって、 同じプログラムを複数回書くという手間を省くことができました。 処理の方法に名前をつけて定義しておけば、あとはそれを何回でも実行できます。

また、大きなプログラムをひとまとめに書くと理解するのが大変です。 メソッドによって大きな仕事を部分に分け、 それぞれを別々に理解することによって、 プログラムを分かりやすくすることができます。

例題: メソッドに情報を与える

メソッドに指示を与えるために、 メソッドの実行の際に情報を与えて処理を行うことができます。

次の例題は三角形を表示するプログラムです。

class TriangleWithMethod {
    public static void main(String[] args) {
        printAsterisk(1);
        printAsterisk(2);
        printAsterisk(3);
        printAsterisk(4);
    }

    static void printAsterisk(int number) {
        for(int i = 0; i < number; i++) 
            System.out.print("*");
        System.out.println();
    }
}

メソッドmainの、 メソッドprintAsteriskの実行部分に注目しましょう。

printAsterisk(1)

メソッドprintAsteriskは、 カッコ内に指定された回数ぶん * マークを表示するメソッドとします。 このように、メソッドに対して、 メソッドを実行する側から数値や文字などのデータを送ることができます。 このようなデータを「引数 (ひきすう, arguments)」と呼びます。

一方、メソッドprintAsteriskの宣言部分に注目します。

static void printAsterisk(int number) {

int number は、 このメソッドはint型の値1つを受け取って仕事をするものだ、 ということを意味しています。 またこの部分は、 実行元 (この例題ではmain側) での引数の型 (int, double, Stringなどの別) と 一致させておく必要があります。

number はint型の値を入れることができる変数の名前です。 普通の変数との違い、このメソッドの実行を開始したとき、 実行元 (この例題ではmain側) で引数に指定された値が あらかじめ入っていることになります。 したがって、numberの値ぶん * マークを表示すれば、 目的の仕事が達成できるというわけです。

メソッドの宣言

実は、今までのプログラムに必ず現れた main もメソッドの一つです。 一般にプログラムは、次のようにいくつかのメソッドを並べた形をしています。 メソッド一つ一つの宣言は、 main と同列に並べて書けば良いのです。

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[] など) を書き、 続けて配列名を書きます。

メソッドの本体の書き方はこれまでのプログラムと同じです。 通常の変数宣言や、文を並べて書けば上から順に実行されることになります。

また、メソッドは宣言された順序や場所に関わらず、 実行元から呼び出されたときにはじめて実行されることに注意してください。

例えば、次のようなプログラムがあったとします。

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を起点とし そのメソッドが実行元から実行された順です。 テキスト形式でプログラムを書いた順番ではありません。

引数

引数 (ひきすう) は、 メソッドの実行元からメソッド内へ値を引き渡す手段です。

次のプログラムを見てください。

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 の値を変化させながら処理を行っていますが、 メソッドの実行元である メソッドmaini には影響はありません。

ただし、この規則には例外があります。 配列や文字列などを引数とした場合、 メソッド内で仮引数の値を変えると、 実行元から見た実引数の値も変化してしまいます。 このように、 引数を使った値の受渡しについては、少し変則的な決まりがあります。 詳しい説明は後まわしにしますが、頭の隅に入れておいてください。

変数の有効範囲

メソッドを用いたプログラムの中には様々な変数が登場することになります。 しかし、プログラムのどこからでも変数が使えるわけではありません。 変数の使える有効範囲のことをスコープと呼びましたね。

変数は、宣言された最も近い中カッコの中でのみ使うことができる、 という規則を思い出してください。

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();
    }
}

この例では、メソッドmainの中 (中カッコの内側) で、 宣言された変数 i は、 mainの中でのみ有効な変数です。 一方、メソッドprintAsteriskにも同じ名前の変数 i が 使われています。 これらは、名前は同じでも有効範囲が異なるため、全く違う変数として使われます。 main側でiの値を変更しても、 printAsterisk側ではiの値に影響はありません。

また、printAsterisk中の変数numberは、 このメソッド内でのみ有効な変数となります。

例題: 三角形を表示する(2)

次のような三角形を画面に表示するプログラムを作成します。

*.....
**....
*.*...
*..*..
*...*.
******

三角形をアステリスク (*) で表示し、 それ以外の背景をピリオド (.) で埋めつくしています。 このような出力をするにはアステリスクやピリオドを複数個連続して 出力するという作業を何回も行う必要があります。 この部分をメソッドとして独立させれば良いということになります。

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);
    }
}