オーバーライド(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("こんにちは");
}
}
オーバーライドとオーバーロードの違い
名前が似ているので混同しがちですが、継承またはインターフェースの実装が絡む場合はオーバーライド、単純に同じクラス内などで引数違いのメソッドを用意する場合はオーバーロードと把握すると明確になります。
オーバーライド時の注意点
- シグニチャの一致
- メソッド名、引数の個数と型、戻り値の型が同じであることが必要です。
- ただし、Java 5 以降では共変戻り値型(親のメソッドの戻り値が
Animalなら、子のメソッドの戻り値をDogにするなど)を許容しています。(参考 共変戻り値とは?)
- アクセス修飾子の制約
- オーバーライド先(子クラス側)のアクセス修飾子を、元のメソッドより狭めることはできません。
- 親が
public→ 子もpublic(もしくは同等) - 親が
protected→ 子はprotectedかpublic - 親が
privateはそもそもオーバーライドできません(継承先から見えないため)。
- 例外宣言の制約
- チェック例外をスローする場合、オーバーライドメソッドで宣言する例外は、親のメソッドが宣言する例外と同じまたはそのサブクラスでなければなりません。
- 例外の数や種類を追加して広げることはできません。
@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!"
}
}
- 親クラス
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でなければコンパイルエラーになります。
