Java | オブジェクト指向:@Override アノテーション

Java Java
スポンサーリンク

何をするアノテーションか

@Override は「このメソッドは親のメソッドをオーバーライド(上書き)しています」とコンパイラに伝えるための印です。付けておくと、シグネチャの食い違いやタイプミス、オーバーロードとの取り違えなどをコンパイル時に検出できます。動作を変える魔法ではなく、間違いを“早期に”見つけるための強力な安全装置です。


どこで使えるかと基本ルール

クラスの継承でのオーバーライド

親クラスのメソッドを子クラスで置き換えるときに付けます。戻り値の共変(より具体的な型へ狭める)は許されますが、メソッド名・引数(型と並び)は親と一致している必要があります。

abstract class Shape {
    abstract double area();
}

final class Rect extends Shape {
    @Override
    double area() { return w * h; }   // 正しいオーバーライド
    Rect(double w, double h) { this.w = w; this.h = h; }
    private final double w, h;
}
Java

インターフェース実装にも使える

インターフェースのメソッドを実装する際も @Override を付けられます。default メソッドを上書きする場合も同様です。

interface Describable {
    String describe();
}

final class User implements Describable {
    @Override
    public String describe() { return "User"; } // 実装に @Override を付ける
}
Java

何が防げるのか(重要ポイントの深掘り)

オーバーライドのつもりが“オーバーロード”になる事故

引数や戻り値をうっかり変えてしまうと、同名の別メソッド(オーバーロード)になって、親のメソッドが呼ばれ続けます。@Override を付けていれば「親と一致していない」とコンパイルエラーになります。

class Base { void send(String s) {} }

class Sub extends Base {
    // @Override を付けるとここはコンパイルエラー(引数型が違うため)
    void send(Object o) {} // オーバーロードになってしまう
}
Java

タイプミスやアクセス修飾子の不一致

メソッド名のスペルミスや、親より狭いアクセス修飾子(public→protected など)への変更は、契約違反として検出されます。動作前に必ず止まるので安全です。

class Base { public void greet() {} }

class Sub extends Base {
    @Override
    protected void greet() {} // コンパイルエラー(公開範囲を狭められない)
}
Java

例外宣言の広げすぎ

チェック例外(throws)の宣言を親より広げるのは不可。@Override を付けていれば、ここもコンパイル時に指摘されます。


付けても意味がない/付けられない場面

final・static・private メソッドは対象外

  • final は上書き不可
  • static はクラスメソッドで“隠蔽”になり、動的に切り替わらない
  • private は外から見えないため、そもそもオーバーライドできない

これらに @Override を付けることはできません。

class Base {
    final void lock() {}
    static void util() {}
    private void internal() {}
}
Java

新規メソッドや間違ったシグネチャ

親に存在しないメソッドに @Override を付けると、即コンパイルエラーになります。これが「意図と実装のズレ」を抑止します。


super と合わせた典型パターン

親の処理を拡張する

親のメソッドに前後の処理を足したいとき、@Override と super の組み合わせが定番です。テンプレートメソッドやフック実装でよく使います。

class Base {
    void render() { System.out.println("header"); }
}
class Sub extends Base {
    @Override
    void render() {
        super.render();                 // 親の前処理
        System.out.println("body");     // 追加
    }
}
Java

よくある混同と回避のコツ(重要ポイントの深掘り)

オーバーロードとの違いを固定する

「引数の違い=オーバーロード」「完全一致=オーバーライド」。迷ったら必ず @Override を付ける。コンパイラが“本当に上書きできているか”を保証してくれます。

戻り値の共変はOK、広げるのはNG

より具体的な型に狭める(Animal→Cat)は可。ただし、より広い型に変える(Cat→Animal)と互換性違反になります。

class Animal {}
class Cat extends Animal {}
class Factory { Animal create() { return new Animal(); } }

class CatFactory extends Factory {
    @Override
    Cat create() { return new Cat(); }  // 共変戻り値(OK)
}
Java

インターフェース実装でも忘れない

インターフェースを実装するメソッドにも @Override を付ける習慣を徹底すると、タイポやシグネチャのズレを早期に検出できます(default メソッドの上書きも同様)。


例題で身につける

例 1: 正しいオーバーライドと誤りの差

abstract class Shape { abstract double area(); }

final class Circle extends Shape {
    @Override
    double area() { return Math.PI * r * r; } // OK
    Circle(double r) { this.r = r; }
    private final double r;
}

final class BadCircle extends Shape {
    // @Override を付けるとコンパイルエラー(戻り値型が違う)
    int area() { return (int)(Math.PI * r * r); } // 誤り:シグネチャ不一致
    BadCircle(int r) { this.r = r; }
    private final int r;
}
Java

例 2: インターフェースの default を上書き

interface Describable {
    default String describe() { return "unknown"; }
}

final class Product implements Describable {
    @Override
    public String describe() { return "Product"; } // default を上書き
}
Java

例 3: 契約違反(公開範囲を狭める)

class Base {
    public void run() {}
}
class Sub extends Base {
    @Override
    void run() {} // コンパイルエラー(public→package-privateへは狭められない)
}
Java

仕上げのアドバイス(重要部分のまとめ)

@Override は「意図したオーバーライドが本当に成立しているか」を機械的に保証するためのマーカーです。常に付けて、オーバーロードとの取り違え・タイプミス・公開範囲や例外宣言の互換性違反をコンパイル時に潰しましょう。クラス継承でもインターフェース実装でも付ける習慣を徹底するだけで、ポリモーフィズムのバグが激減します。

タイトルとURLをコピーしました