08_メソッド

対象:C言語経験者 / C言語の「関数」がJavaでは「メソッド」になります。構文の差分と、クラスの中に書くという点を理解します


■ 1. C言語の「関数」とJavaの「メソッド」

やっていることはC言語の関数と同じです。処理をひとまとめにして名前を付け、呼び出して使います。

Javaでは「クラスの中に定義された関数」のことを メソッド と呼びます。

C言語(関数)
// ファイルに直接書く
int add(int a, int b) {
    return a + b;
}

int main(void) {
    int result = add(3, 4);
    printf("%d\n", result);
    return 0;
}
Java(メソッド)
// クラスの中に書く
public class Calc {

    static int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = add(3, 4);
        System.out.println(result);
    }
}

C言語との主な違いは2点です。

項目C言語Java
書く場所ファイルに直接書く必ずクラスの中に書く
修飾子なしstaticpublic などが付く
プロトタイプ宣言必要(呼び出しより前に書かない場合)不要(同じクラス内ならどこに書いてもよい)
戻り値なしvoidvoid(同じ)

■ 2. メソッドの構文

static int add( int a, int b) { 修飾子 戻り値の型 名前 引数(型 変数名)
C言語の関数定義と基本的に同じ。static などの修飾子が加わります。

● static とは(今の段階では)

static を付けると「インスタンスを作らなくても呼び出せるメソッド」になります。
クラスやインスタンスについては次の単元で詳しく学びます。今の段階では 「メソッドを定義するときは static を付ける」 と覚えておきましょう。

main メソッドが public static void main になっているのも同じ理由です。JVMがインスタンスを作らずに呼び出せるよう static が付いています。

● プロトタイプ宣言が不要な理由

C言語では呼び出しより後ろに関数を書くとプロトタイプ宣言が必要でしたが、Javaでは不要です。コンパイラがクラス全体を見渡して解決してくれます。

public class Example {

    public static void main(String[] args) {
        greet("Taro");       // main より後ろに定義されていてもOK
    }

    static void greet(String name) {
        System.out.println("Hello, " + name + "!");
    }
}

■ 3. 引数の渡し方(値渡し)

Javaのプリミティブ型の引数はC言語と同じ 値渡し です。メソッド内で変更しても呼び出し元には影響しません。

public class PassValue {

    static void doubleIt(int x) {
        x = x * 2;    // ローカルの x を変えているだけ
        System.out.println("メソッド内: x = " + x);
    }

    public static void main(String[] args) {
        int a = 5;
        doubleIt(a);
        System.out.println("呼び出し元: a = " + a);   // a は変わらない
    }
}
メソッド内: x = 10 呼び出し元: a = 5

● 配列は参照渡しのように動作する

配列(参照型)を渡すと、参照(アドレス)のコピー が渡されます。メソッド内で要素を変更すると、呼び出し元の配列にも反映されます。

プリミティブ型(int など) 呼び出し元 a=5 コピー メソッド内 x=5→10 別のコピーなので 呼び出し元は変わらない a=5 変わらず 配列(参照型) 呼び出し元 0x3f2 メソッド内 0x3f2 アドレスのコピー [5,3,8] 同じ実体を指しているので 要素を変えると呼び出し元にも反映
プリミティブ型は値のコピーが渡ります。配列はアドレスのコピーが渡るため、メソッド内での変更が元の配列に反映されます。
public class PassArray {

    static void fill(int[] arr, int val) {
        for (int i = 0; i < arr.length; i++) {
            arr[i] = val;   // 呼び出し元の配列を直接変更している
        }
    }

    public static void main(String[] args) {
        int[] data = {1, 2, 3};
        fill(data, 99);
        System.out.println(data[0]);   // → 99(変わっている)
    }
}

■ 4. 戻り値

C言語と全く同じです。return で値を返します。戻り値がない場合は void

// 戻り値あり
static int max(int a, int b) {
    if (a >= b) {
        return a;
    } else {
        return b;
    }
}

// void(戻り値なし)
static void printLine(int n) {
    for (int i = 0; i < n; i++) {
        System.out.print("-");
    }
    System.out.println();
    // return; は省略可
}

// 使い方
System.out.println(max(3, 7));   // → 7
printLine(10);                    // → ----------

■ 5. メソッドのオーバーロード Java追加

同じ名前のメソッドを 引数の型や数を変えて複数定義 できます。これをオーバーロードと言います。C言語には同名関数を複数定義する機能はありません。

public class Overload {

    // int 型の加算
    static int add(int a, int b) {
        return a + b;
    }

    // double 型の加算(同じ名前でOK)
    static double add(double a, double b) {
        return a + b;
    }

    // 3つの引数(同じ名前でOK)
    static int add(int a, int b, int c) {
        return a + b + c;
    }

    public static void main(String[] args) {
        System.out.println(add(1, 2));          // → 3    (int版)
        System.out.println(add(1.5, 2.5));      // → 4.0  (double版)
        System.out.println(add(1, 2, 3));       // → 6    (3引数版)
    }
}
コンパイラが引数の型と数を見て、どのメソッドを呼ぶか自動的に判断します。
System.out.println() が int・double・String など何でも受け取れるのも、内部でオーバーロードされているからです。
戻り値の型だけが違うオーバーロードはできません。
static int add(int a, int b)static double add(int a, int b) は引数が同じなのでコンパイルエラーになります。

■ 実習1:メソッドの基本を確認する

実習1

08_Methods フォルダに Methods.java を作って実行しましょう。

public class Methods {

    // ① 値を返すメソッド
    static int max(int a, int b) {
        return (a >= b) ? a : b;
    }

    // ② void メソッド(罫線を引く)
    static void printBorder(int n) {
        for (int i = 0; i < n; i++) {
            System.out.print("=");
        }
        System.out.println();
    }

    // ③ 配列を受け取るメソッド(参照渡し)
    static int sum(int[] arr) {
        int total = 0;
        for (int x : arr) {
            total += x;
        }
        return total;
    }

    // ④ オーバーロード
    static String repeat(String s, int n) {
        String result = "";
        for (int i = 0; i < n; i++) {
            result += s;
        }
        return result;
    }

    static String repeat(String s) {
        return repeat(s, 3);   // 引数1つ版はデフォルト3回
    }

    public static void main(String[] args) {
        printBorder(20);
        System.out.println("max(5, 8) = " + max(5, 8));
        System.out.println("max(9, 3) = " + max(9, 3));
        printBorder(20);

        int[] scores = {85, 62, 91, 47, 78};
        System.out.println("合計: " + sum(scores));
        System.out.println("平均: " + (double) sum(scores) / scores.length);

        printBorder(20);
        System.out.println(repeat("Java ", 4));
        System.out.println(repeat("Ha"));     // 引数1つ版:3回
    }
}

実行結果:

==================== max(5, 8) = 8 max(9, 3) = 9 ==================== 合計: 363 平均: 72.6 ==================== Java Java Java Java HaHaHa

● 動いたら変えてみましょう

#変えてみること確認ポイント
1 ③ の sum メソッド内で arr[0] = 999; を追加してから scores[0] を出力する 配列の参照渡しの動作として、呼び出し元の scores[0] が 999 に変わることを確認しましょう
2 max メソッドを double 版でオーバーロードして max(3.14, 2.71) を呼ぶ 引数の型で自動的に正しいメソッドが呼ばれることを確認しましょう
3 戻り値の型だけ違う同名メソッドを書いてみる(例:static double sum(int[] arr) コンパイルエラーになることを確認しましょう

■ 実習2:試験問4のパターンを再現する

実習2

09_MethodExam フォルダに MethodExam.java を作って実行しましょう。

第67回 問4(26) に登場したメソッド呼び出しパターンです。引数の型に注目しましょう。

public class MethodExam {

    // String と boolean を受け取るメソッド
    static void over30(String s, boolean b) {
        System.out.println(s + " : " + b);
    }

    public static void main(String[] args) {
        String name = "Taro";
        int age = 32;

        // age > 30 は boolean 型の式なので第2引数に渡せる
        over30(name, age > 30);
    }
}

実行結果:

Taro : true
なぜ age > 30 が引数として渡せるのか確認しましょう

age > 30 は比較演算子の式で、結果は boolean 型(true または false)になります。

メソッド over30 の第2引数は boolean b なので、型が一致して渡すことができます。

試験第67回 問4(26) では「age, age - 30」という選択肢は第1引数が int 型なので不正解、「name, age > 30」が正解になります。


■ 試験(問4・問6)との対応

頻出ポイント

よく問われること正しい答え
メソッドの定義場所必ずクラスの中。C言語のようにファイルに直接は書けません
プロトタイプ宣言不要。同じクラス内ならどの順番でも呼び出せます
プリミティブ型の引数値渡し。メソッド内で変更しても呼び出し元は変わりません
配列の引数参照のコピーが渡るため、要素の変更は呼び出し元に反映されます
オーバーロードの条件引数の型・数が違えばOK。戻り値の型だけの違いはコンパイルエラー
static の意味インスタンスを作らずに呼び出せるメソッド
練習問題:次の出力を答えましょう(第67回 問4(26) 類似)
public class Q {

    static void over30(String s, boolean b) {
        System.out.println(s + " : " + b);
    }

    public static void main(String[] args) {
        String name = "taro";
        int age = 32;
        over30(name, age > 30);
    }
}
taro : true

age > 3032 > 30 なので trueboolean 型として第2引数に渡されます。


■ まとめ

ポイント内容
メソッド = 関数C言語の関数と同じ概念。クラスの中に書く点が違います
staticインスタンス不要で呼べるメソッド。今の段階では必ず付けます
プロトタイプ不要コンパイラがクラス全体を見渡すため宣言不要です
値渡しプリミティブ型はコピーが渡ります。元の変数は変わりません
配列の引数アドレスのコピーが渡ります。要素変更は呼び出し元に反映されます
オーバーロード引数の型・数が違えば同じ名前のメソッドを複数定義できます