25.

Java 継承の構文完全ガイド(extends / super / @Override / abstract / sealed)

編集
この記事の要点
  • Java の継承は class Child extends Parent単一継承のみ (多重継承は interface で代替)
  • 親コンストラクタ呼び出しは super(...)。子コンストラクタの先頭で 1 回だけ
  • メソッドオーバーライドには @Override アノテーションを付けてシグネチャ誤りを防ぐ
  • 継承禁止は final class / final method、抽象クラスは abstract class
  • Java 17+ は sealed で継承可能なクラスを列挙可能

extends による継承

Java では extends で親クラスを 1 つだけ継承します。すべてのクラスは暗黙のうちに java.lang.Object を継承しているため、extends Object は省略可能です。

// 親クラス
public class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public void speak() {
        System.out.println(name + " makes a sound");
    }
}

// 子クラス
public class Dog extends Animal {
    public Dog(String name) {
        super(name);   // 親コンストラクタを呼ぶ (省略すると引数なしを暗黙呼び出し)
    }

    @Override
    public void speak() {
        System.out.println(name + " says Woof");
    }
}

// 利用
Animal a = new Dog("Pochi");
a.speak();   // Pochi says Woof   ← 動的束縛 (ポリモーフィズム)

super キーワード

用法説明
super(...)親コンストラクタ呼び出し。子コンストラクタの最初の文で 1 回だけ
super.method()親のメソッドを呼ぶ (オーバーライド後でも親の実装にアクセス)
super.field親のフィールド参照 (同名で隠蔽されている場合)
public class Cat extends Animal {
    private int lives;

    public Cat(String name, int lives) {
        super(name);          // 親コンストラクタ
        this.lives = lives;
    }

    @Override
    public void speak() {
        super.speak();        // 親の speak() も呼ぶ
        System.out.println(name + " says Meow");
    }
}

@Override アノテーション

オーバーライド時は必ず @Override を付けます。シグネチャを間違えるとコンパイルエラーになり、タイポによる「オーバーライドしたつもり」事故を防げます。

public class Bird extends Animal {
    // ❌ シグネチャ間違い: 親には speak() しかない
    @Override
    public void speek() {   // typo!
        // → error: method does not override or implement a method from a supertype
    }

    // ✅ 正しいオーバーライド
    @Override
    public void speak() {
        System.out.println(name + " sings");
    }
}

final と abstract

// final class: これ以上継承できない
public final class String { ... }   // 標準ライブラリの String も final

// final method: オーバーライド禁止
public class Service {
    public final void log(String msg) { ... }   // 子で上書きできない
}

// abstract class: インスタンス化できない、子で実装が必要
public abstract class Shape {
    public abstract double area();   // 実装なし。子で必須実装

    public void print() {            // 通常メソッドも書ける
        System.out.println("area=" + area());
    }
}

public class Circle extends Shape {
    private double r;
    public Circle(double r) { this.r = r; }

    @Override
    public double area() { return Math.PI * r * r; }
}

new Shape();   // ❌ error: Shape is abstract
new Circle(5); // ✅ OK

interface による多重継承の代替

Java はクラスの多重継承を禁止。代わりに interface を複数 implements できます。

public interface Flyable {
    void fly();
    default void glide() {   // Java 8+ default メソッド
        System.out.println("gliding");
    }
}

public interface Swimmable {
    void swim();
}

// 複数 interface を実装可能
public class Duck extends Animal implements Flyable, Swimmable {
    public Duck(String n) { super(n); }

    @Override public void fly()  { System.out.println(name + " flies"); }
    @Override public void swim() { System.out.println(name + " swims"); }
}

Diamond Problem

同名 default メソッドを持つ 2 つの interface を実装すると衝突するため、明示的に解決が必要:

interface A { default void hello() { System.out.println("A"); } }
interface B { default void hello() { System.out.println("B"); } }

class C implements A, B {
    // どちらを採用するか明示しないとコンパイルエラー
    @Override
    public void hello() {
        A.super.hello();   // A を採用
    }
}

static メソッドはオーバーライドできない

class Parent {
    public static void hi() { System.out.println("Parent.hi"); }
}

class Child extends Parent {
    // これは hiding (隠蔽)、override ではない
    public static void hi() { System.out.println("Child.hi"); }
}

Parent p = new Child();
p.hi();   // → "Parent.hi" (型でディスパッチされる)

Java 17+ : sealed class

継承を一部のクラスにだけ許可する機構。代数的データ型 (sum type) を表現できます。

public sealed class Shape
    permits Circle, Rectangle, Triangle { }

public final class Circle extends Shape { ... }
public final class Rectangle extends Shape { ... }
public non-sealed class Triangle extends Shape { ... }   // ここから先は自由継承

// switch パターン (Java 21)
String name = switch (shape) {
    case Circle c    -> "circle";
    case Rectangle r -> "rect";
    case Triangle t  -> "tri";
};   // sealed なので default 不要 (網羅チェック)

コンストラクタチェーン

class A {
    A() { System.out.println("A()"); }
}
class B extends A {
    B() {
        // super(); が暗黙で呼ばれる
        System.out.println("B()");
    }
}
class C extends B {
    C() {
        System.out.println("C()");
    }
}

new C();
// 出力:
// A()
// B()
// C()

FAQ

Q: なぜ Java は多重継承を禁止?
A: Diamond Problem の曖昧さを避けるため。interface で代替できるし、Java 8 default メソッドで実装も持てるので実用上問題ない。

Q: protectedpackage-private の違いは?
A: protected同パッケージ + サブクラス、package-private (修飾子なし) は同パッケージのみ。継承先からアクセスさせたいなら protected。

Q: 継承より委譲 (composition) を使うべき?
A: 一般論として yes。Effective Java 「継承よりコンポジション」。継承は親実装の変更が子に波及する脆さがある。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本事項
  2. HTMLへの埋め込み
  3. 変数
  4. 可変変数
  5. 定数
  6. データ型
  7. キャスト
  8. エスケープ文字
  9. 配列
  10. 演算子
  11. 代入の際の注意点
  12. 条件分岐
  13. 繰り返し処理
  14. クラスとインスタンス
  15. コンストラクタ
  16. 関数
  17. スーパーグローバル変数
  18. スコープ
  19. staticについて
  20. yieldについて
  21. ファイルのアップロード方法
  22. DB接続方法
  23. SQL実行方法
  24. カプセル化の具体例
  25. 継承の構文
  26. オーバーライド
  27. ポリモーフィズム(多様性)の具体例
  28. 抽象クラス・メソッドの構文と具体例
  29. GET通信
  30. try catchで全てのエラーを拾う方法

最近更新/作成されたページ