12.

Java メソッド完全ガイド — 修飾子・オーバーロード・参照

編集
この記事の要点
  • Java のメソッド宣言: 修飾子 戻り値型 メソッド名(引数) { 本体 }
  • アクセス修飾子: public / protected / package-private / private。その他修飾子: static / final / abstract / synchronized
  • 同名で引数違いはオーバーロード、親と同じシグネチャの再定義はオーバーライド (@Override 必須)
  • 可変長引数 String... で 0 個以上を受け取れる。内部的には配列
  • Java 8+ の関数型 interface とラムダ式 / メソッド参照 String::length、interface の default / static メソッド

メソッドの基本構文

public class Calculator {
    // 修飾子 戻り値型 メソッド名(引数リスト)
    public int add(int a, int b) {
        return a + b;
    }

    // 戻り値なしは void
    public void printSum(int a, int b) {
        System.out.println(a + b);
    }

    // 引数なし・戻り値なし
    public void hello() {
        System.out.println("Hello");
    }
}

アクセス修飾子の比較

修飾子同クラス同パッケージサブクラスその他
public
protected×
(なし)package-private××
private×××

その他の修飾子

public class Sample {
    // static: インスタンス不要で呼べる
    public static int square(int x) { return x * x; }

    // final: サブクラスでオーバーライド禁止
    public final void critical() { /* 変更させない処理 */ }

    // synchronized: スレッド排他
    public synchronized void increment() { /* ... */ }

    // native: JNI で C/C++ 実装
    public native void osCall();

    // abstract: 抽象メソッド(本体なし、abstract クラスでのみ可)
    // public abstract void run();
}

// 呼び出し
int n = Sample.square(5);       // static は ClassName.method
Sample s = new Sample();
s.critical();                   // インスタンスメソッド

メソッドオーバーロード

同じ名前で引数の型または数が違うメソッドを複数定義できます。戻り値型だけ違うのはオーバーロードとして認められません。

public class Printer {
    public void print(int n) { System.out.println("int: " + n); }
    public void print(double d) { System.out.println("double: " + d); }
    public void print(String s) { System.out.println("String: " + s); }
    public void print(String s, int times) {
        for (int i = 0; i < times; i++) System.out.println(s);
    }
}

new Printer().print(10);          // int 版
new Printer().print(3.14);        // double 版
new Printer().print("hi");        // String 版
new Printer().print("hi", 3);     // 2 引数版

メソッドオーバーライド

親クラスのメソッドを子クラスで再定義します。シグネチャを完全一致させる必要があり、@Override アノテーションでタイポを検出できます。

class Animal {
    public String cry() { return "..."; }
}

class Dog extends Animal {
    @Override                       // タイポ防止に必須
    public String cry() { return "Wan!"; }
}

class Cat extends Animal {
    @Override
    public String cry() { return "Nya-"; }
}

Animal a = new Dog();
a.cry();                            // "Wan!" — 動的ディスパッチ

可変長引数 (varargs)

public static int sum(int... nums) {
    int s = 0;
    for (int n : nums) s += n;
    return s;
}

sum();                  // 0
sum(1);                 // 1
sum(1, 2, 3, 4, 5);     // 15
sum(new int[]{1, 2, 3}); // 配列で渡しても OK

// 通常引数と併用可(varargs は必ず最後)
public static String join(String sep, String... parts) { /* ... */ }
join(",", "a", "b", "c"); // "a,b,c"

関数型 interface とラムダ式 (Java 8+)

抽象メソッドが 1 つだけの interface を関数型 interfaceと呼び、@FunctionalInterface アノテーションでマークします。ラムダ式で短く実装できます。

@FunctionalInterface
interface Calc {
    int apply(int a, int b);
}

// 匿名クラス(旧来)
Calc addOld = new Calc() {
    @Override public int apply(int a, int b) { return a + b; }
};

// ラムダ式
Calc add = (a, b) -> a + b;
Calc mul = (a, b) -> a * b;
add.apply(2, 3);   // 5

// 標準ライブラリの関数型 interface
java.util.function.Function len = String::length;
java.util.function.Predicate notEmpty = s -> !s.isEmpty();
java.util.function.Consumer printer = System.out::println;
java.util.function.Supplier now = System::currentTimeMillis;

メソッド参照

既存のメソッドを関数型 interface のインスタンスとして直接渡せます。ラムダの短縮形と考えてよいです。

記法意味ラムダ等価
ClassName::staticMethodstatic メソッド参照(x) -> ClassName.staticMethod(x)
ClassName::instanceMethodクラスの非 static メソッド(obj, x) -> obj.instanceMethod(x)
instance::method特定インスタンスのメソッド(x) -> instance.method(x)
ClassName::newコンストラクタ参照(x) -> new ClassName(x)
List list = List.of("banana", "apple", "cherry");
list.stream()
    .map(String::toUpperCase)       // (s) -> s.toUpperCase()
    .sorted()
    .forEach(System.out::println);  // (s) -> System.out.println(s)

interface の default / static メソッド (Java 8+)

interface Greeter {
    String name();

    // default: 実装を持つ。既存実装クラスを壊さず機能追加
    default String hello() {
        return "Hello, " + name();
    }

    // static: interface 自身に属する
    static Greeter of(String n) {
        return () -> n;
    }
}

Greeter g = Greeter.of("Taro");
g.hello();                          // "Hello, Taro"

FAQ

Q: 戻り値型だけ違うオーバーロードはなぜ不可?
A: 呼び出し側がどちらを呼ぶか決められないため。シグネチャに戻り値型は含まれません。

Q: static メソッドはオーバーライドできる?
A: 厳密にはオーバーライドではなく隠蔽 (hiding)。同名 static を子クラスに定義しても多態は効きません。

Q: ラムダ式と匿名クラスの違いは?
A: ラムダはthis が外側のクラスを指す、追加フィールドを持てない。匿名クラスは独立した内部クラス。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本的なルール
  2. データ型
  3. 変数
  4. 定数
  5. 配列
  6. コレクション(List,Set,Queue)
  7. Map(連想配列)
  8. 演算子
  9. 条件分岐
  10. 繰り返し制御文
  11. クラス
  12. メソッド
  13. インスタンス化
  14. コンストラクタ
  15. staticキーワード
  16. オーバーロード
  17. 継承
  18. オーバーライド
  19. this
  20. super
  21. パッケージ
  22. アクセス修飾子
  23. 抽象クラス・メソッド
  24. インターフェース
  25. カプセル化
  26. データベース接続
  27. セッション
  28. ファイル入出力
  29. ラムダ式