この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:6
ページ更新者:爽健
更新日時:2026-06-11 07:10:02

タイトル: 継承の構文
SEOタイトル: 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 「継承よりコンポジション」。継承は親実装の変更が子に波及する脆さがある。