CodeGym /コース /JAVA 25 SELF /継承の概念、extends の構文

継承の概念、extends の構文

JAVA 25 SELF
レベル 17 , レッスン 0
使用可能

1. 継承とは何か?

継承は、既存のクラスを基にしてそのプロパティやメソッドを「受け継ぎ」、新しいクラスを作る仕組みです。新しいクラスはサブクラス(子クラスとも)、元のクラスは親クラス(または基底クラス)と呼ばれます。

たとえば古いレシピをベースに新しいケーキを作るようなものです。良いところはすべて受け継ぎ、材料を少し追加して——はい、「おばあちゃんのレシピ+秘密の材料のケーキ」の出来上がり、という感じです。

  • 親クラス — 共通のプロパティと振る舞いを持ちます。
  • サブクラス — それらを継承し、さらに独自のものを追加したり振る舞いを変更できます。

たとえば「猫は動物である」: すべての猫は動物ですが、すべての動物が猫ではありません。動物を基底クラス、猫をそのサブクラスにできます。同様に犬もサブクラスにできます。
また「円は図形である」: すべての円は図形ですが、すべての図形が円ではありません。図形を親クラス、円や三角形をその継承側にできます。Java ではこれらの関係をキーワード extends で表現します。

なぜ継承が必要なのか?

よくある疑問: なぜ単にあるクラスのコードを別のクラスへコピペするだけではいけないのか? なぜ継承が生まれたのか?

  • コードの再利用: 共通部分は基底クラスにまとめます。フィールドやメソッドを重複させる必要はなく、サブクラスに自動的に現れます。たとえば大学向けのアプリでは、人を基底クラスにして少なくとも名前と誕生日を持たせ、「学生」「教員」をそれぞれサブクラスとして独自のプロパティを追加できます。
  • 拡張と特化: 必要なところだけ新しいプロパティやメソッドを追加したり、既存の振る舞いを変更できます。すべての動物は鳴きますが、猫はニャー、犬はワンと鳴く、といった具合です。また、猫には木登りの能力を追加できます(基底クラスにはない能力)。
  • 階層の構造化: コードがより論理的で分かりやすくなります。たとえばクラス AnimalDogCat があれば、DogCatAnimal の特殊化であることが一目で分かります。

例. もし Java に継承がなければ、eat()sleep() のようなメソッドやフィールド name を動物ごとのクラスにすべてコピーしなければなりません。さらに明日 breathe() メソッドを追加したくなったら、何十ものクラスを修正する羽目になります! 継承があれば、呼吸の機能を基底クラスに追加するだけで済みます。

2. 継承の構文: extends

Java で継承を宣言するには? とても簡単です。キーワード extends を使います。

class RoditelskiyKlass {
    // フィールドとメソッド
}

class Podklass extends RoditelskiyKlass {
    // 追加のフィールドとメソッド
}

例 1: 動物

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

    void eat() {
        System.out.println(name + " は食べています。");
    }
}

// サブクラス
class Cat extends Animal {
    void meow() {
        System.out.println(name + " はこう言う: ニャー!");
    }
}

ここで何が起きている?

  • クラス Cat はクラス Animal のすべてのフィールドとメソッドを継承します。
  • つまり、Cat は明示的に書かなくてもフィールド name とメソッド eat() を既に持っています。
  • 加えて Cat には独自のメソッド meow() が追加されます。

使用例

public class Main {
    public static void main(String[] args) {
        Cat kitty = new Cat();
        kitty.name = "Barsik";
        kitty.eat();      // Barsik は食べています。  (継承済み)
        kitty.meow();     // Barsik はこう言う: ニャー! (独自メソッド)
    }
}

できました! 重複を避け、最小限のコードで新しいクラスを手に入れました。

3. サブクラスは具体的に何を継承するか?

Java でサブクラスが継承するのは次のとおりです:

  • publicprotected のフィールドとメソッド(基底クラスのもの)。
  • package-private のフィールドとメソッド(同一パッケージ内であれば)。

継承されないもの:

  • コンストラクタ(サブクラスで明示的に宣言する必要があります)。
  • private のフィールドとメソッド(それらは基底クラスの内部でのみアクセス可能)。
  • static 初期化ブロック(継承はされませんが、各クラスが独自に持てます)。
  • package-private のメンバー(サブクラスが別パッケージにある場合)。

イラスト

class Animal {
    public String name;
    private int age;

    public void eat() { 
        System.out.println("食べる"); 
    }
    private void secret() { 
        System.out.println("秘密!"); 
    }
}

class Dog extends Animal {
    public void bark() { 
        System.out.println("ワン!"); 
    }
}
  • Dog はフィールド name とメソッド eat() を継承します。
  • Dog からはフィールド age とメソッド secret()見えません — どちらも private です。

4. Java における継承の制約

Java はわりと厳格で、いくつかルールがあります。

クラスの単一継承のみ: 各クラスはただ 1 つのクラスからしか継承できません。ただし、そのクラスに対して複数のサブクラスを作ることは可能です。

class Dog extends Animal { ... } // OK
class Dog extends Animal, Pet { ... } // エラー! これはできません

インターフェースの多重継承は可能ですが、これは別のトピックなので後で扱います.

クラス Object は Java のすべてのクラスの祖先です。たとえ明示的に extends を書かなくても、あなたのクラスは暗黙に Object を継承します。

5. 実用的な例

「動物」アプリを作り続けてみましょう。

ステップ 1. 基底クラス Animal

class Animal {
    String name;

    void eat() {
        System.out.println(name + " は食べています。");
    }
}

ステップ 2. サブクラス Cat

class Cat extends Animal {
    void meow() {
        System.out.println(name + " はこう言う: ニャー!");
    }
}

ステップ 3. サブクラス Dog

class Dog extends Animal {
    void bark() {
        System.out.println(name + " はこう言う: ワン!");
    }
}

ステップ 4. クラスを使ってみる

public class Main {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.name = "Murka";
        cat.eat();   // Murka は食べています。
        cat.meow();  // Murka はこう言う: ニャー!

        Dog dog = new Dog();
        dog.name = "Sharik";
        dog.eat();   // Sharik は食べています。
        dog.bark();  // Sharik はこう言う: ワン!
    }
}

何が得られたか?

  • CatDog の両方が、基底クラスからフィールド name とメソッド eat() を共用しています。
  • 各クラスは独自の振る舞いを追加します。猫には meow()、犬には bark()
  • 動物の食べ方を変更したい場合は、Animal クラスの eat() を変更するだけで、すべての継承先に自動的に反映されます。

6. 役立つ要点

可視化: クラス階層

階層は次のように表せます:

.        Animal
        /    \
     Cat     Dog
  • Animal — 基底クラスで、動物に共通するものを含みます。
  • CatDog — サブクラスで、固有の特徴を追加します。

コンストラクタと継承について少し

コンストラクタは継承されません。サブクラスでコンストラクタを定義しない場合、Java は自動的に引数なしコンストラクタを追加します(親にそれがある場合)。親クラスに引数つきコンストラクタしかない場合は、サブクラスで super(...) を使って明示的に呼び出す必要があります。短い例:

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

class Cat extends Animal {
    Cat(String name) {
        super(name); // 親クラスのコンストラクタを呼び出す
    }
}

7. 継承を使う際の典型的なミス

誤りその1: 複数のクラスから同時に継承しようとする。
Java では class Cat extends Animal, Pet のようには書けません。親は 1 つだけです!

誤りその2: private のフィールドやメソッドがサブクラスから見えると期待する。
private メンバーは継承されません(正確には存在しますが、サブクラスからアクセスできません)。継承先にアクセスさせたい場合は protected を使いましょう。

誤りその3: 親のコンストラクタ呼び出しを忘れる。
基底クラスに引数なしコンストラクタがなく、かつ super(...) を明示的に呼び出さないと、コンパイルエラーになります。

誤りその4: 「コードを節約したいから」という理由だけで継承を使う。
クラス間に「is-a(〜である)」の関係がないなら、継承は使うべきではありません。たとえば「人間」は「自動車」から継承すべきではありません。たとえ両者に speed というフィールドがあっても、です。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION