オーバーライド(Override) とは、継承やインターフェース実装によって引き継いだメソッドの実装を、子クラスや実装クラスで再定義することを指します。
オーバーライドの基本知識
オーバーライドの定義
- オーバーライド(Override) とは、継承やインターフェース実装によって引き継いだメソッドの実装を、子クラスや実装クラスで再定義することを指します。
オーバーライドの意義
- 親クラス(スーパークラス)やインターフェースで宣言・定義されているメソッドを子クラス(サブクラス)や実装クラスで上書き(override) できることで、同じメソッド呼び出しでも違う処理を行う可能性が生まれます。
- 多態性(ポリモーフィズム) の実現における重要な仕組みの一つです。
注意点:同名メソッドとの違い
- 同じクラス内でたとえメソッド名が同じであっても、継承関係が存在しなければオーバーライドではなく、単に別のメソッドです(オーバーロードは後述)。
なぜオーバーライドが必要か?
柔軟な実装を可能にする
例えば、親クラス側の実装では一般的な処理を定義し、子クラスではそれを上書きしてより専門的な振る舞いに変えることができます。これによって、共通部分は親クラスにまとめつつ、個別のクラスで挙動を変えるという柔軟性が得られます。
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("こんにちは"); } }
オーバーライドとオーバーロードの違い
名前が似ているので混同しがちですが、継承またはインターフェースの実装が絡む場合はオーバーライド、単純に同じクラス内などで引数違いのメソッドを用意する場合はオーバーロードと把握すると明確になります。
オーバーライド時の注意点
- シグニチャの一致
- メソッド名、引数の個数と型、戻り値の型が同じであることが必要です。
- ただし、Java 5 以降では共変戻り値型(親のメソッドの戻り値が
Animal
なら、子のメソッドの戻り値をDog
にするなど)を許容しています。
- アクセス修飾子の制約
- オーバーライド先(子クラス側)のアクセス修飾子を、元のメソッドより狭めることはできません。
- 親が
public
→ 子もpublic
(もしくは同等) - 親が
protected
→ 子はprotected
かpublic
- 親が
private
はそもそもオーバーライドできません(継承先から見えないため)。
- 例外宣言の制約
- チェック例外をスローする場合、オーバーライドメソッドで宣言する例外は、親のメソッドが宣言する例外と同じまたはそのサブクラスでなければなりません。
- 例外の数や種類を追加して広げることはできません。
@Override
アノテーションの活用- オーバーライドするメソッドには、明示的に
@Override
アノテーションを付けるのが一般的です。 - 付けることで、コンパイラが「これは正しいオーバーライドか」をチェックしてくれます。
- オーバーライドするメソッドには、明示的に
class Parent { void show() {} } class Child extends Parent { @Override void show() { // 再定義した処理 } }
実際の例を見てみよう
クラスの継承によるオーバーライド
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!" } }
- 親クラス
Vehicle
のrun()
メソッドを、子クラス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
でなければコンパイルエラーになります。