Javaのオーバーライドを徹底解説|基本ルール・@Override・実務の注意点
CONTENTS
Java開発において、「オーバーライド(Override)」はオブジェクト指向プログラミングの核心とも言える重要な機能です。
しかし、学習を始めたばかりの方からは
「書き方は分かるけれど、どのような場面で使うのかイメージできない」「オーバーロードと名前が似ていて混乱する」
という声をよく耳にします。
一方で、実務の現場ではオーバーライドの理解不足が深刻なバグを引き起こすことも少なくありません。
特に @Override アノテーションの扱い一つで、保守性が大きく変わります。
本記事では、オーバーライドの基本から実務で役立つ書き方、そして絶対に避けるべき落とし穴までを体系的に解説します。
オーバーライドとは|仕組みと目的
オーバーライドとは、一言で言えば「親クラス(スーパークラス)で定義されたメソッドを、子クラス(サブクラス)で上書きして再定義すること」を指します。
例えば、親クラスが「動物」で、子クラスが「犬」や「猫」だった場合を想像してみてください。
「鳴く」という共通の動作であっても、犬は「ワン」、猫は「ニャー」と振る舞いが異なります。
このように、同じメソッド名でもオブジェクトによって異なる動作をさせる仕組みを「多態性(ポリモーフィズム)」と呼びます。
したがって、オーバーライドはこの多態性を実現するために不可欠な技術です。
オーバーライドが成立するための厳格なルール
Javaにおいて、メソッドが正しくオーバーライドされるためには、以下の条件をすべて満たす必要があります。
- メソッド名が完全に一致していること
- 引数の型・数・並び順が完全に一致していること
- 戻り値の型が同じ、またはそのサブクラスであること(共変戻り値)
- アクセス修飾子が親クラスと同じか、それ以上に広いこと
さらに、以下のケースではオーバーライドができないため注意が必要です。
final修飾子がついたメソッド(変更禁止)staticメソッド(インスタンスに紐付かないため)privateメソッド(子クラスから見えないため)
基本的な書き方とコード例
実際に、親クラス Animal のメソッドを、子クラス Dog でオーバーライドする例を見てみましょう。
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 myDog = new Dog();
myDog.speak(); // 出力結果: ワン!
}
}
ここで重要なのは、変数の型が Animal であっても、実体が Dog であれば、オーバーライドされた側のメソッド(ワン!)が実行されるという点です。
これを「動的バインディング」と呼びます。
オーバーロードとの決定的な違い
多くの初心者が混同しやすいのが「オーバーライド(Override)」と「オーバーロード(Overload)」です。名前は似ていますが、その役割は全く異なります。
以下の表でそれぞれの特徴を整理しました。
| 観点 | オーバーライド | オーバーロード |
|---|---|---|
| 対象 | 継承関係(親と子) | 同一クラス内 |
| メソッド名 | 同じ | 同じ |
| 引数 | 完全一致が必要 | 型・数が異なる必要がある |
| 目的 | 振る舞いの上書き(機能変更) | 使いやすさ向上(多重定義) |
オーバーロードは、例えば println() メソッドのように、「文字列も数値も同じメソッド名で出力したい」という利便性のために使われます。
より詳細な言語仕様については、Java公式言語仕様のオーバーライドセクションも併せて参照することをおすすめします。
実務で必須となる@Overrideアノテーション
結論から言うと、オーバーライドをする際は必ず @Override アノテーションを記述すべきです。
文法上は省略可能ですが、実務開発においてこれを省略することは「バグの温床」となり、コードレビューでも指摘の対象となります。
理由1:コンパイルエラーでミスに気づける
例えば、親クラスのメソッド名をタイプミスしてしまった場合や、引数の型を間違えた場合を考えてみましょう。
@Override があれば、コンパイラが「親クラスにそんなメソッドはない」とエラーを出してくれます。しかし、記述がない場合、コンパイラは「新しいメソッドが追加された(オーバーロード)」と解釈してしまい、エラーになりません。
class Dog extends Animal {
// ❌ ミス:メソッド名を間違えている(speak -> speek)
// @Override がない場合:
// エラーにならず、speek()という新メソッドとして扱われる。
// 結果、本来の speak() は上書きされず、バグの原因に。
void speek() {
System.out.println("ワン!");
}
// @Override がある場合:
// 「メソッドが見つかりません」とコンパイルエラーになる!
@Override
void speek() { ... }
}
理由2:コードの意図が明確になる
@Override が付いていることで、コードを読む第三者が「これは親クラスの挙動を変更している箇所だ」と一目で理解できます。
可読性を高め、保守コストを下げるためにも、アノテーションは省略せずに記述しましょう。
Java公式APIドキュメントのOverrideアノテーション解説でも推奨されています。
よくある落とし穴と注意点
最後に、実務で遭遇しやすいオーバーライド関連のトラブルを2つ紹介します。
staticメソッドはオーバーライドできない
static(静的)メソッドはクラスに紐付くものであり、インスタンスに紐付くオーバーライドの対象外です。
子クラスで同名の static メソッドを定義することは可能ですが、これは「隠蔽(Hiding)」と呼ばれ、オーバーライドとは異なる挙動をします。
したがって、多態性が効かないため、変数の型によって呼び出されるメソッドが固定されてしまいます。
アクセス修飾子の制限
オーバーライドする際、「親クラスよりもアクセス権を厳しくする(狭める)」ことはできません。
- 親:
public→ 子:protected❌ (エラー) - 親:
protected→ 子:public✅ (OK)
これは、「親クラスとして扱える場所なら、子クラスも同様に扱えなければならない」というオブジェクト指向の原則(リスコフの置換原則)を守るためです。
まとめ

Javaのオーバーライドについて、基本から実務的な注意点まで解説しました。
- オーバーライドは、親クラスの振る舞いを子クラスで変更する機能
- オーバーロードとは目的も仕組みも全く別物
- @Override は記述必須。タイプミスや仕様変更時のバグを防ぐ命綱となる
- アクセス修飾子やstaticメソッドの扱いに注意する
正しくオーバーライドを使いこなすことで、変更に強く、拡張性の高いプログラムを設計できるようになります。
まずは手元のコードで @Override を付け、意図通りに動くか試してみることから始めてみましょう。
つまずいたら、現役エンジニアと一緒に解決しよう
この記事で扱った【Java】は、独学だと細かい仕様で詰まりやすい分野です。さらに理解を深めたい方は、Zerocode Onlineで実務直結の学習を進めてみませんか。
コード添削・詰まり解消を丁寧にサポート。
Java/SQL/Spring Bootを体系的に習得。
現場で刺さる成果物づくりを伴走。
時間と場所を選ばず学べます。