PR

【Java】オーバーライド(Override)の基本と注意点を3分でわかりやすく

Java

オーバーライド(Override) とは、継承やインターフェース実装によって引き継いだメソッドの実装を、子クラスや実装クラスで再定義することを指します。

  • ポイント
    • オーバーライドは「継承」があるときにしか行えません。
    • オーバーライドする①メソッド名・②引数リスト・③戻り値の型は、親クラスやインターフェースで定義されたメソッドと完全に一致している必要があります(Java 5以降は、戻り値の型の共変戻り値型(Covariant Return Types)を一部許容する仕様がありますが、基本的には整合が必要です)。
    • オーバーライド先のアクセス修飾子は、元のメソッドより狭くしてはいけません。例)親メソッドが public なら、子メソッドを protectedprivate にはできません。
    • アノテーション @Override を書くと、オーバーライドが正しくできているかコンパイル時にチェックしてくれます。
class Animal {
    void speak() {
        System.out.println("動物が鳴く");
    }
}

class Dog extends Animal {
    @Override
    void speak() {
        System.out.println("ワンワン");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.speak();  // 出力: ワンワン
    }
}

参考 クラスの継承 / インターフェース

スポンサーリンク

オーバーライド(@Override)とは?

オーバーライドの定義

  • オーバーライド(Override) とは、継承やインターフェース実装によって引き継いだメソッドの実装を、子クラスや実装クラスで再定義することを指します。
class Animal {
    void sound() {
        System.out.println("なにかの動物の声");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("にゃーにゃー");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.sound();  // 出力: なにかの動物の声

        Animal cat = new Cat();
        cat.sound();     // 出力: にゃーにゃー ← オーバーライドされたメソッドが呼ばれる!
    }
}

🧾 解説:ざっくり理解!

  • Animalクラスが元(親クラス)で、「sound()」というメソッドを持ってます。
  • Catクラスがそれを継承していて、同じメソッド名で中身を書きかえてます(=オーバーライド)。
  • 実行してみると、new Cat() を使ったときは 「にゃーにゃー」 と、Catクラスのメソッドが使われる

つまり、「親のメソッドを、子が自分流にアレンジする」のがオーバーライドです。

なぜオーバーライドが必要か?

柔軟な実装を可能にする

例えば、親クラス側の実装では一般的な処理を定義し、子クラスではそれを上書きしてより専門的な振る舞いに変えることができます。これによって、共通部分は親クラスにまとめつつ、個別のクラスで挙動を変えるという柔軟性が得られます。

class Animal {
    void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Bark!");
    }
}

実装の共通化と差異化

インターフェースを実装するクラスでも、オーバーライドによって実装の“違い”を生み出せます。複数のクラスが同一インターフェースを実装していても、各クラスで処理を異なる形で提供できるため、コードの共通化差異化を両立できます。

interface Greeting {
    void sayHello();
}

class EnglishGreeting implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("Hello");
    }
}

class JapaneseGreeting implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("こんにちは");
    }
}

オーバーライドとオーバーロードの違い

  • オーバーライド(Override)
    • 継承元のメソッドを、シグニチャ(メソッド名、引数リスト、戻り値の型)を同じにして再定義すること。
    • ポイント:継承やインターフェースの実装が前提。
  • オーバーロード(Overload)
    • 同じクラス内で、メソッド名は同じだが引数リストが異なるメソッドを定義すること。
    • 例:void doSomething(), void doSomething(String param), int doSomething(int x, int y) など。
    • ポイント:引数の数や型が違うことでメソッドを複数定義する手法。

名前が似ているので混同しがちですが、継承またはインターフェースの実装が絡む場合はオーバーライド単純に同じクラス内などで引数違いのメソッドを用意する場合はオーバーロードと把握すると明確になります。

オーバーライド時の注意点

  1. シグニチャの一致
    • メソッド名、引数の個数と型、戻り値の型が同じであることが必要です。
    • ただし、Java 5 以降では共変戻り値型(親のメソッドの戻り値が Animal なら、子のメソッドの戻り値を Dog にするなど)を許容しています。(参考 共変戻り値とは?
  2. アクセス修飾子の制約
    • オーバーライド先(子クラス側)のアクセス修飾子を、元のメソッドより狭めることはできません。
    • 親が public → 子も public (もしくは同等)
    • 親が protected → 子は protectedpublic
    • 親が private はそもそもオーバーライドできません(継承先から見えないため)。
  3. 例外宣言の制約
    • チェック例外をスローする場合、オーバーライドメソッドで宣言する例外は、親のメソッドが宣言する例外と同じまたはそのサブクラスでなければなりません。
    • 例外の数や種類を追加して広げることはできません。
  4. @Override アノテーションの活用
    • オーバーライドするメソッドには、明示的に @Override アノテーションを付けるのが一般的です。
    • 付けることで、コンパイラが「これは正しいオーバーライドか」をチェックしてくれます。
class Parent {
    void show() {}
}

class Child extends Parent {
    @Override
    void show() {
        // 再定義した処理
    }
}

オーバーライドの実例

ここからはクラスの継承によるオーバーライドと、インターフェースの実装によるオーバーライドの実例をお見せします。以下のコードを理解できれば一旦の基本理解としてはOKです。

クラスの継承によるオーバーライド

class Vehicle {
    void run() {
        System.out.println("The vehicle is running.");
    }
}

class Car extends Vehicle {
    @Override
    void run() {
        System.out.println("The car is running fast!");
    }
}

public class Main {
    public static void main(String[] args) {
        Vehicle myCar = new Car();
        myCar.run();  
        // 出力: "The car is running fast!"
    }
}
  • 親クラス Vehiclerun() メソッドを、子クラス Car で再定義している例です。
  • @Override を付けることで、オーバーライドであることが明示されます(省略した場合も動作しますが、付けたほうがエラーに気付きやすくなるので推奨されます)。

インターフェースの実装によるオーバーライド

interface Printable {
    void print();
}

class Book implements Printable {
    @Override
    public void print() {
        System.out.println("Printing a book...");
    }
}

public class Main {
    public static void main(String[] args) {
        Printable p = new Book();
        p.print();  
        // 出力: "Printing a book..."
    }
}
  • インターフェース Printable が定義しているメソッド print() を、クラス Book において実装している例です。
  • インターフェースのメソッドはデフォルトで public なので、実装する側も public でなければコンパイルエラーになります。

まとめ オーバーライドとは?

  • オーバーライド(Override) は、親クラス(あるいはインターフェース)のメソッドを、子クラス(あるいは実装クラス)側で再定義することを指します。
  • オーバーロード(Overload) は、同じクラス内で、メソッド名は同じだが引数リストが異なるメソッドを複数定義することを指します。オーバーライドとは別の概念なので混同しないように注意しましょう。
  • オーバーライド時は、シグニチャが一致しているか、アクセス修飾子例外宣言などの制約に違反していないかを確認してください。
  • @Override アノテーションを使うことで、コンパイルレベルでのエラー検知や可読性向上につながります。
タイトルとURLをコピーしました