タイトル: オーバーライド
SEOタイトル: Java メソッドオーバーライド完全ガイド (@Override / 可視性 / 共変戻り値)
| この記事の要点 |
|
オーバーライドの基本
親クラスのメソッドを、子クラスで同じ名前・引数・戻り値の型 (互換) で再定義することをオーバーライド (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 も「常に付けろ」と推奨。