26.

Java メソッドオーバーライド完全ガイド (@Override / 可視性 / 共変戻り値)

編集
この記事の要点
  • オーバーライド = 親クラスのメソッドを子クラスで同じシグネチャで再定義
  • @Override アノテーション必須級。タイポ検知になる
  • 可視性は狭めることはできない (public → public/protected 不可は protected → public OK)
  • 戻り値は共変 (covariant) 可 (Java 5+)。例外は狭めることのみ可
  • private / static / final はオーバーライド不可super.method() で親を呼べる

オーバーライドの基本

親クラスのメソッドを、子クラスで同じ名前・引数・戻り値の型 (互換) で再定義することをオーバーライド (override) と言います。実行時の実際の型に応じて呼び出されるメソッドが決まる動的ディスパッチの仕組みです。

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

class Dog extends Animal {
    @Override
    public String sound() {  // ★ オーバーライド
        return "Wan!";
    }
}

class Cat extends Animal {
    @Override
    public String sound() {
        return "Nyan!";
    }
}

Animal a1 = new Dog();
Animal a2 = new Cat();
a1.sound();  // "Wan!"  ← 実行時の型 (Dog) で決まる
a2.sound();  // "Nyan!"

@Override アノテーション

必須ではないが事実上必須。タイポやシグネチャ違いをコンパイル時に検知してくれます。

class Parent {
    public void process(String s) { ... }
}

// ❌ タイポ。新メソッド扱いされる
class Child1 extends Parent {
    public void proces(String s) { ... }  // typo に気付けない
}

// ✅ @Override でコンパイルエラー
class Child2 extends Parent {
    @Override
    public void proces(String s) { ... }
    // → コンパイルエラー: method does not override
}

// ✅ シグネチャ違いも検知
class Child3 extends Parent {
    @Override
    public void process(Object o) { ... }
    // → コンパイルエラー: 引数型違い
}

オーバーライドのルール

項目ルール
メソッド名同じ
引数 (順序・型・数)同じ
戻り値同じ or 共変 (サブ型 OK)
可視性 (アクセス修飾子)狭めるのは NG。広げるのは OK
throws 例外同じ or 狭める (サブクラス例外のみ)
private / static / finalオーバーライド不可

可視性のルール

class Parent {
    protected void foo() { ... }
}

// ✅ public に広げるのは OK
class Child1 extends Parent {
    @Override
    public void foo() { ... }
}

// ❌ private に狭めるのは NG
class Child2 extends Parent {
    @Override
    private void foo() { ... }  // コンパイルエラー
}

// public > protected > package > private の順で広い

共変戻り値 (Covariant Return Type, Java 5+)

class AnimalFactory {
    public Animal create() {
        return new Animal();
    }
}

// ✅ 戻り値を Dog (Animal のサブ型) に
class DogFactory extends AnimalFactory {
    @Override
    public Dog create() {  // 戻り値共変
        return new Dog();
    }
}

DogFactory df = new DogFactory();
Dog d = df.create();  // キャスト不要

throws 例外のルール

class Parent {
    public void read() throws IOException { ... }
}

// ✅ 同じ例外 OK
class Child1 extends Parent {
    @Override public void read() throws IOException { ... }
}

// ✅ サブクラス例外に狭める OK
class Child2 extends Parent {
    @Override public void read() throws FileNotFoundException { ... }
}

// ✅ 例外を投げないのも OK
class Child3 extends Parent {
    @Override public void read() { ... }
}

// ❌ 新しい checked 例外を追加 NG
class Child4 extends Parent {
    @Override public void read() throws IOException, SQLException { ... }
    // コンパイルエラー
}

super で親メソッドを呼び出す

class Logger {
    public void log(String msg) {
        System.out.println("[LOG] " + msg);
    }
}

class TimestampLogger extends Logger {
    @Override
    public void log(String msg) {
        super.log("[" + System.currentTimeMillis() + "] " + msg);
        // ★ super で親メソッドを呼ぶ
    }
}

class FileLogger extends TimestampLogger {
    @Override
    public void log(String msg) {
        super.log(msg);     // TimestampLogger.log → Logger.log
        writeToFile(msg);
    }
}

オーバーライドできないメソッド

class Parent {
    public final void cannotOverride() { ... }      // final
    public static void staticMethod() { ... }       // static
    private void privateMethod() { ... }            // private
}

class Child extends Parent {
    // ❌ final はオーバーライド不可
    // @Override public void cannotOverride() { ... }

    // ❌ static はオーバーライドではなく「隠蔽」
    public static void staticMethod() { ... }  // hides ではなく overrides ではない

    // ❌ private は親に存在しない扱い (子は new メソッド)
    public void privateMethod() { ... }  // @Override 付けるとエラー
}

abstract / interface メソッドの実装

abstract class Shape {
    abstract double area();      // 抽象メソッド
}

class Circle extends Shape {
    private double r;

    @Override
    double area() {
        return Math.PI * r * r;
    }
}

// インターフェースの実装も @Override 推奨
interface Drawable {
    void draw();
}

class Square implements Drawable {
    @Override
    public void draw() { ... }
}

オーバーロード vs オーバーライド

項目オーバーロード (Overload)オーバーライド (Override)
意味同名で引数違い親メソッドの再定義
クラス関係同じクラス内親子クラス間
引数異なる同じ
戻り値同じでも違ってもよい同じ or 共変
多態性静的 (コンパイル時)動的 (実行時)
@Override付けない★ 付ける
class Calculator {
    int add(int a, int b) { return a + b; }
    double add(double a, double b) { return a + b; }    // オーバーロード
    int add(int a, int b, int c) { return a + b + c; }  // オーバーロード
}

Object クラスの定番オーバーライド

public class User {
    private final long id;
    private final String name;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User u = (User) o;
        return id == u.id;
    }

    @Override
    public int hashCode() {
        return Long.hashCode(id);
    }

    @Override
    public String toString() {
        return "User{id=" + id + ", name='" + name + "'}";
    }
}

// equals と hashCode は必ず一緒に override (HashMap キーで使えなくなる)

FAQ

Q: static メソッドをオーバーライドしたい
A: できません。同じシグネチャの static メソッドを子クラスで定義すると「メソッド隠蔽 (hiding)」になり、動的ディスパッチされません。

Q: コンストラクタはオーバーライドできる?
A: 不可。コンストラクタはクラス固有でサブクラスに継承されません。super(...) で親コンストラクタを呼び出します。

Q: @Override が無いとエラー?
A: エラーにはなりません。ただし付けないとタイポを検知できないので IDE が警告します。Effective Java も「常に付けろ」と推奨。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本事項
  2. HTMLへの埋め込み
  3. 変数
  4. 可変変数
  5. 定数
  6. データ型
  7. キャスト
  8. エスケープ文字
  9. 配列
  10. 演算子
  11. 代入の際の注意点
  12. 条件分岐
  13. 繰り返し処理
  14. クラスとインスタンス
  15. コンストラクタ
  16. 関数
  17. スーパーグローバル変数
  18. スコープ
  19. staticについて
  20. yieldについて
  21. ファイルのアップロード方法
  22. DB接続方法
  23. SQL実行方法
  24. カプセル化の具体例
  25. 継承の構文
  26. オーバーライド
  27. ポリモーフィズム(多様性)の具体例
  28. 抽象クラス・メソッドの構文と具体例
  29. GET通信
  30. try catchで全てのエラーを拾う方法