MEDIA

メディア

  1. TOP
  2. メディア
  3. プログラミング
  4. Javaの継承とは?コード再利用の仕組みと実務での注意点をやさしく解説

Javaの継承とは?コード再利用の仕組みと実務での注意点をやさしく解説

Javaを学び始めると、「クラス」や「メソッド」の次に必ずと言っていいほど登場するのが「継承(inheritance)」という概念です。

継承を適切に使えば、コードの重複を劇的に減らし、修正や拡張に強いプログラムを作ることができます。しかし、仕組みを正しく理解せずに使うと、逆に複雑で扱いにくいコードを生んでしまう原因にもなりかねません。

そこで本記事では、Javaの継承の基本構文から、実務で必須となる「オーバーライド」「super」の使い方、そして現場レベルの設計判断までを網羅して解説します。

「なぜ継承が必要なのか?」という根本的な疑問を解消し、自信を持ってコードを書けるようになりましょう。

Javaの継承とは?親クラスの機能を子が受け継ぐ仕組み

継承とは、既存のクラス(親)が持つフィールドやメソッドを、新しいクラス(子)が引き継ぐ仕組みのことです。

Javaでは、新しく作るクラスに extends キーワードを付けることで継承を行います。これによって、親クラスで定義済みの機能を子クラス側でわざわざ書き直す必要がなくなります。

継承における用語の整理(スーパークラスとサブクラス)

継承関係にあるクラスは、一般的に次のように呼ばれます。

  • スーパークラス(親クラス):継承元となるクラス。基本的な機能を持つ。
  • サブクラス(子クラス):継承して作られるクラス。親の機能を使いつつ、独自の機能を追加する。

例えば、「動物(Animal)」という大きな枠組みがあり、その具体例として「犬(Dog)」や「猫(Cat)」がいるとします。この場合、Animalがスーパークラス、DogやCatがサブクラスにあたります。

基本構文とコード例

実際にコードを見てみましょう。まず、基本となる親クラスを作成します。

// スーパークラス
class Animal {
    void walk() {
        System.out.println("歩いています");
    }
}

次に、この Animal クラスを継承して Dog クラスを作ります。

// サブクラス(extendsで継承)
class Dog extends Animal {
    // 中身は空っぽでもOK
}

public class Main {
    public static void main(String[] args) {
        Dog pochi = new Dog();
        pochi.walk(); // Animalクラスのメソッドが使える
    }
}

このように、Dog クラスには何も記述していなくても、extends Animal と書くだけで walk() メソッドが使えるようになります。

したがって、共通の処理を親クラスに一度書くだけで、すべての子クラスで再利用できるのです。

継承を使う3つのメリット

継承は単に「コードを書く量が減る」だけではありません。システム全体の品質を高めるための重要な役割を持っています。

1. コードの再利用性が高まり保守が楽になる

もし継承がなければ、DogにもCatにもBirdにも、同じ「歩く処理」をコピー&ペーストしなければなりません。

しかし、継承を使えば親クラスの修正だけで済みます。例えば「歩く」処理にログ出力を追加したい場合、Animal クラスを1箇所修正するだけで、数百個あるかもしれない子クラスすべてに修正が反映されます。

2. クラス間に「Is-a関係」が生まれ構造が明確になる

継承は「B is a A(BはAの一種である)」という関係性をコードで表現します。

  • Dog is a Animal(犬は動物の一種である)
  • Car is a Vehicle(車は乗り物の一種である)

このように、プログラムの登場人物(クラス)たちがどのような分類になっているかが、コードを見るだけで直感的に分かるようになります。

3. 多態性(ポリモーフィズム)の基礎となる

実務において継承が最も威力を発揮するのは、この「多態性」と組み合わせたときです。

例えば、DogCat も元をたどれば Animal です。そのため、異なるクラスであっても「Animal型」としてまとめて扱うことができます。

// Animal型の変数に、サブクラスのインスタンスを代入できる
Animal myDog = new Dog();
Animal myCat = new Cat();

// 配列などでまとめて管理できる
Animal[] animals = { new Dog(), new Cat() };

for (Animal a : animals) {
    a.walk(); // それぞれのクラスの振る舞いが実行される
}

その結果、将来的に新しい動物が増えても、呼び出し側のコード(ループ処理など)を修正する必要がなくなります。

メソッドのオーバーライド(Override)による拡張

親クラスのメソッドをそのまま使うだけでなく、子クラス側で独自の内容に書き換えることを「オーバーライド(上書き)」と呼びます。

オーバーライドの書き方

メソッド名、戻り値の型、引数のリストを親クラスと全く同じにして定義し直します。

class Dog extends Animal {
    // 親のwalkメソッドを上書き
    @Override
    void walk() {
        System.out.println("犬が四つ足で走っています");
    }
}

@Override アノテーションの重要性

上記のコードにある @Override は、「これはオーバーライドしたメソッドですよ」とコンパイラに伝えるための印です。

これを付けておくと、もしメソッド名のスペルミス(例:wolk())をした場合に、コンパイラが「親クラスにそんなメソッドはないのでオーバーライドできていません」とエラーを出してくれます。したがって、バグを防ぐためにも、必ず付ける習慣をつけましょう。

参考:Oracle Java Documentation - Overriding and Hiding Methods

superキーワードとコンストラクタの仕組み

継承を学ぶ上で、初学者が最もつまずきやすいのが「コンストラクタ」の動きと super キーワードです。

superで親のメソッドを呼び出す

オーバーライドしたけれど、親クラスの元の処理も実行したい場合には super を使います。

class Dog extends Animal {
    @Override
    void walk() {
        super.walk(); // 親の「歩いています」を実行
        System.out.println("さらに、尻尾を振っています");
    }
}

この仕組みは、フレームワークなどで「基本機能(ログ記録や初期化)」を親クラスに残しつつ、独自処理を追加したい場合によく使われます。

コンストラクタの暗黙的な呼び出し

Javaのルールとして、「子クラスのインスタンスを作るとき、必ず先に親クラスのコンストラクタが実行される」という鉄則があります。

class Animal {
    Animal() {
        System.out.println("Animalが生成されました");
    }
}

class Dog extends Animal {
    Dog() {
        // ここには見えない super(); が存在する
        System.out.println("Dogが生成されました");
    }
}

// 実行結果:
// Animalが生成されました
// Dogが生成されました

子クラスのコンストラクタの先頭には、明示的に書かなくても super();(親の引数なしコンストラクタ呼び出し)が自動的に挿入されます。

もし親クラスに「引数ありのコンストラクタ」しか定義されていない場合、子クラス側で明示的に super(引数); を書かないとコンパイルエラーになるため注意が必要です。

実務での継承の注意点と「継承よりコンポジション」

ここまで継承の便利さを解説しましたが、近年のソフトウェア開発では「継承の使いすぎは危険」と言われることが増えています。

なぜなら、不適切な継承はクラス間の結合度を強め、変更に弱いシステムを作ってしまうからです。

1. 親クラスの変更が子クラスすべてに波及する

親クラスのメソッドの挙動を変えたとき、それを継承しているすべての子クラスに影響が出ます。意図せず子クラスの機能が壊れてしまう現象は「壊れやすい基底クラス問題(Fragile Base Class Problem)」として知られています。

2. 「Is-a関係」が成立しない継承は避ける

「コードを共通化したいから」という理由だけで、全く関係のないクラスを継承させてはいけません。

例えば、「リスト機能を使いたいから」といって、Employee(従業員)クラスが ArrayList を継承するのは間違いです。従業員はリストの一種ではないからです。このような場合は、継承ではなくフィールドとしてリストを持つべきです。

3. コンポジション(委譲)の活用

柔軟な設計が必要な場合、継承よりもコンポジション(Composition)が推奨されます。これは「継承する」のではなく、「クラスの中で別のクラスの機能を使う」という設計です。

// 継承ではなく、フィールドとして持つ(Has-a関係)
class Dog {
    private Animal animal = new Animal(); // コンポジション

    void walk() {
        animal.walk(); // 処理を委譲する
    }
}

こうすることで、クラス同士の結びつき(依存度)を弱め、部品の交換が容易なプログラムになります。

まとめ|継承は強力だが用法用量を守って使おう

Javaの継承は、オブジェクト指向プログラミングの根幹を支える強力な機能です。正しく使えば、重複のない美しいコードを実現できます。

  • extends で親クラスの機能を引き継ぐ。
  • @Override で振る舞いをカスタマイズする。
  • super で親の機能やコンストラクタを利用する。
  • 「Is-a関係」が成り立たない場合は継承を避ける。

まずは基本的な構文と動作をマスターし、慣れてきたら「インターフェース」や「抽象クラス」との使い分けも学んでいくと、Javaの設計力が飛躍的に向上するでしょう。

もし実務レベルの設計で迷った際は、Googleなどのスタイルガイドも参考にしてみてください。

参考:Google Java Style Guide

つまずいたら、現役エンジニアと一緒に解決しよう

この記事で扱った【Java 継承 / extends / オーバーライド】は、独学では特につまずきやすい領域です。さらに理解を深めたい方は、Zerocode Onlineで実務直結の学習を進めてみませんか。

現役エンジニア1on1

コード添削・詰まり解消を丁寧にサポート。

実務設計まで網羅

Java/SQL/Spring Bootを体系的に習得。

ポートフォリオ支援

現場で刺さる成果物づくりを伴走。

完全オンライン

時間と場所を選ばず学べます。

Join us! 未経験からエンジニアに挑戦できる環境で自分の可能性を信じてみよう 採用ページを見る→

記事監修

ドライブライン編集部

[ この記事をシェアする ]

記事一覧へ戻る