【Java】継承の基本とsuperキーワードの使い方をわかりやすく解説

Java

こんにちは、かつコーチです。
ここまでクラス・オブジェクト・カプセル化を解説してきましたが、今回は複数のクラスに共通する部分をまとめられる「継承」と、セットで使うsuperキーワードを解説します。

継承とは?

共通部分を親クラスにまとめる仕組み

継承とは、あるクラスが別のクラスのフィールドやメソッドを引き継ぐ仕組みのことです。
引き継ぐ元のクラスを「親クラス(スーパークラス)」、引き継ぐ側のクラスを「子クラス(サブクラス)」と呼びます。

例えば「犬」と「猫」はどちらも「動物」の一種で、名前や年齢、鳴くという動作を共通して持っています。

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

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

    public void eat() {
        System.out.println(name + "がごはんを食べています");
    }
}
// 子クラス
public class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age); // 親クラスのコンストラクタを呼び出す
    }

    public void bark() {
        System.out.println(name + "が吠えました!");
    }
}

extendsキーワードを使うことで、DogクラスはAnimalクラスを継承します。
これによりDogクラスはeat()メソッドを自分で書かなくても使えるようになります。

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("ポチ", 3);
        dog.eat();  // Animalクラスのメソッド:ポチがごはんを食べています
        dog.bark(); // Dogクラス独自のメソッド:ポチが吠えました!
    }
}

なぜ継承が必要なのか

継承を使わない場合、DogクラスとCatクラスにそれぞれnameageeat()を重複して書く必要があります。

// 継承なしだと同じコードが重複してしまう
public class Dog {
    String name;
    int age;
    void eat() { System.out.println(name + "がごはんを食べています"); }
    void bark() { System.out.println(name + "が吠えました!"); }
}

public class Cat {
    String name;
    int age;
    void eat() { System.out.println(name + "がごはんを食べています"); } // 重複
    void meow() { System.out.println(name + "がニャーと鳴きました!"); }
}

共通部分をAnimalクラスにまとめて継承すれば、修正が必要になったときも1箇所を直すだけで済みます。
これが継承の大きなメリットである「コードの再利用性」です。

superキーワードの役割

親クラスのコンストラクタを呼び出す

super(...)は、親クラスのコンストラクタを呼び出すための構文です。
子クラスのコンストラクタの先頭で使う必要があります。

public class Dog extends Animal {
    private String breed; // 犬種(子クラス独自のフィールド)

    public Dog(String name, int age, String breed) {
        super(name, age); // Animalのコンストラクタでname, ageを初期化
        this.breed = breed;
    }
}

もしsuper(...)を書かなかった場合、Javaは自動的に親クラスの「引数なしコンストラクタ」を呼び出そうとします。
親クラスに引数なしコンストラクタが存在しない場合は、コンパイルエラーになります。

親クラスのメソッドを呼び出す

super.メソッド名()という形で、親クラスのメソッドを明示的に呼び出すこともできます。
これは、子クラスで同名のメソッドを上書き(オーバーライド)しつつ、親の処理も活かしたい場合によく使われます。

public class Dog extends Animal {
    @Override
    public void eat() {
        super.eat(); // 親クラスのeat()も実行する
        System.out.println("ドッグフードをおかわりしました");
    }
}
ポチがごはんを食べています
ドッグフードをおかわりしました

よくあるつまずきポイント・エラー対処

protectedとprivateの違いでハマる

継承を学び始めた頃、多くの人が親クラスのフィールドをprivateのままにしてしまい、子クラスから使えずに困ります。

❌Before

public class Animal {
    private String name; // 子クラスから直接アクセスできない

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

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    public void bark() {
        System.out.println(name + "が吠えました!"); // コンパイルエラー
    }
}

エラーメッセージは以下のようになります。

error: name has private access in Animal

✅After

子クラスからも直接アクセスさせたいフィールドには、privateではなくprotected(同一パッケージ・子クラスからアクセス可能な修飾子)を使います。

public class Animal {
    protected String name; // 子クラスからもアクセス可能

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

私が初めて継承を使ったとき、最初はすべてのフィールドをprivateにしていたため、子クラスから軒並みアクセスできずコンパイルエラーの山になった経験があります。
「子クラスに使わせたいフィールドかどうか」を意識してprotectedprivateかを選ぶ、という判断基準を持つとハマりにくくなります。

多重継承ができないと勘違いする

Javaのクラスは、複数のクラスを同時に継承すること(多重継承)ができません。

❌Before

// コンパイルエラー:クラスは1つしかextendsできない
public class Dog extends Animal, Pet {
}
error: '{' expected

✅After

複数の特性を持たせたい場合は、後の記事で解説するinterface(インターフェース)を使います。
1つの親クラスを継承しつつ、複数のインターフェースを実装するのがJavaの基本パターンです。

public class Dog extends Animal implements Trainable {
    // ...
}

応用・一歩先の使い方

instanceofで型を確認する

継承関係にあるオブジェクトは、instanceof演算子でその型を確認できます。
これは、複数の子クラスをまとめて扱う際に、特定の型だけ特別な処理をしたい場合に役立ちます。

Animal animal = new Dog("ポチ", 3);

if (animal instanceof Dog dog) { // Java 16以降のパターンマッチング構文
    dog.bark();
}

if (animal instanceof Dog dog)という書き方は、型チェックと同時にキャスト済みの変数dogを得られる、Java 16以降で導入されたパターンマッチングです。
継承の理解が深まったら、次の記事で扱う「オーバーライドとポリモーフィズム」と組み合わせて使えるようになります。

まとめ

この記事のポイント

  • 継承extendsを使い、親クラスのフィールド・メソッドを子クラスに引き継ぐ仕組み
  • super(…)は親クラスのコンストラクタ呼び出し、super.メソッド名()は親クラスのメソッド呼び出しに使う
  • 子クラスからアクセスさせたいフィールドはprivateではなくprotectedにする
  • Javaのクラスは多重継承ができず、複数の特性を持たせたい場合はインターフェースを使う

次に読むべき記事

継承の基本を理解したら、次は子クラスで親クラスのメソッドを上書きする「メソッドのオーバーライドとポリモーフィズム」に進みましょう。

タグ: Java, 初心者向け, オブジェクト指向

タイトルとURLをコピーしました