継承の基本:クラスの再利用

docs

Javaプログラミングを楽しんでるかな?前回はクラスオブジェクトについて学んだよね。今回は、その知識をさらに深めて、Javaの強力な機能の一つである継承について見ていこう。継承をマスターすれば、コードを効率的に再利用できるようになるんだ。

継承ってなんだ?

簡単に言うと、継承とは「あるクラスの性質(フィールドやメソッド)を別のクラスが受け継ぐこと」だよ。例えば、動物には「鳴く」とか「食べる」といった共通の行動があるよね。これを一つ一つすべての動物クラスに書くのは面倒だ。継承を使えば、「動物」という大元のクラスにこれらの共通の性質を書いておけば、そこから派生する「犬」や「猫」のクラスは、改めてそれらを書かなくても「鳴く」「食べる」といった性質を自動的に持てるんだ。

継承を使うと、こんなメリットがあるよ。

  • コードの再利用: 同じようなコードを何度も書く必要がなくなるから、開発効率がアップする。
  • 保守性の向上: 大元のクラスを修正すれば、それを継承しているすべてのクラスにその変更が反映されるから、修正が楽になる。
  • 拡張性の向上: 新しい機能を追加したい時も、既存のクラスを壊さずに新しいクラスを作成して機能を拡張できる。

親と子の関係

継承では、性質を受け継ぐ元のクラスを親クラス(スーパークラス)、性質を受け継ぐ新しいクラスを**子クラス(サブクラス)**と呼ぶよ。子クラスは親クラスの持っているものをすべて引き継ぐだけでなく、自分自身の独自の性質を追加することもできるんだ。

イメージとしては、親から子へ遺伝情報が受け継がれるのと似ているかな。子どもは親の特徴を受け継ぎつつ、自分自身の個性も持っているよね。

継承の書き方

Javaで継承を使うには、extendsキーワードを使うんだ。

Java

class 親クラス名 {
    // 親クラスのフィールドやメソッド
}

class 子クラス名 extends 親クラス名 {
    // 子クラス独自のフィールドやメソッド
}

例えば、前回学んだクラスとオブジェクトの基本を思い出しながら、動物の例で見てみよう。

Java

// 親クラス:Animal
class Animal {
    String name; // 名前

    void eat() {
        System.out.println(name + "は食べる");
    }

    void sleep() {
        System.out.println(name + "は眠る");
    }
}

// 子クラス:Dog
class Dog extends Animal { // Animalクラスを継承
    void bark() {
        System.out.println(name + "はワンワン鳴く");
    }
}

// 子クラス:Cat
class Cat extends Animal { // Animalクラスを継承
    void meow() {
        System.out.println(name + "はニャーニャー鳴く");
    }
}

public class InheritanceExample {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        myDog.name = "ポチ"; // Animalクラスから継承したフィールド
        myDog.eat();       // Animalクラスから継承したメソッド
        myDog.sleep();     // Animalクラスから継承したメソッド
        myDog.bark();      // Dogクラス独自のメソッド

        Cat myCat = new Cat();
        myCat.name = "ミケ"; // Animalクラスから継承したフィールド
        myCat.eat();       // Animalクラスから継承したメソッド
        myCat.sleep();     // Animalクラスから継承したメソッド
        myCat.meow();      // Catクラス独自のメソッド
    }
}

このコードを実行すると、

ポチは食べる
ポチは眠る
ポチはワンワン鳴く
ミケは食べる
ミケは眠る
ミケはニャーニャー鳴く

と表示されるはずだよ。DogクラスもCatクラスも、Animalクラスで定義したnameフィールドやeat()sleep()メソッドを、何も書かずに使うことができているのが分かるよね。これが継承の力なんだ!

コンストラクタと継承

前回、オブジェクトを生成する時に使うコンストラクタについても学んだよね。継承では、親クラスのコンストラクタを子クラスから呼び出す必要があるんだ。これは、親クラスの初期化処理を子クラスが引き継ぐためだよ。

親クラスのコンストラクタを呼び出すには、子クラスのコンストラクタの先頭でsuper()を使うんだ。

Java

class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Personクラスのコンストラクタが呼ばれたよ");
    }

    void introduce() {
        System.out.println("名前は" + name + "、年齢は" + age + "歳です。");
    }
}

class Student extends Person {
    String studentId;

    Student(String name, int age, String studentId) {
        super(name, age); // 親クラス(Person)のコンストラクタを呼び出す
        this.studentId = studentId;
        System.out.println("Studentクラスのコンストラクタが呼ばれたよ");
    }

    void study() {
        System.out.println(name + "は勉強する");
    }
}

public class ConstructorInheritanceExample {
    public static void main(String[] args) {
        Student student = new Student("山田太郎", 18, "S12345");
        student.introduce();
        student.study();
    }
}

このコードを実行すると、

Personクラスのコンストラクタが呼ばれたよ
Studentクラスのコンストラクタが呼ばれたよ
名前は山田太郎、年齢は18歳です。
山田太郎は勉強する

と表示されるよ。Studentクラスのコンストラクタが呼ばれる前に、Personクラスのコンストラクタがsuper(name, age)によって呼び出されているのがわかるね。

メソッドのオーバーライド

子クラスで親クラスと同じ名前のメソッドを定義することもできるんだ。これをオーバーライドというよ。オーバーライドすると、子クラスのオブジェクトからそのメソッドを呼び出したときに、子クラスで定義したメソッドが実行されるんだ。

例えば、Animalクラスにはeat()メソッドがあるけど、Dogクラスではもっと具体的に「ドッグフードを食べる」と表現したいとするよね。そんな時にオーバーライドを使うんだ。

Java

class Animal {
    void eat() {
        System.out.println("何かを食べる");
    }
}

class Dog extends Animal {
    // eat()メソッドをオーバーライド
    @Override // これは「オーバーライドしますよ」という目印。なくても動くけど、書いておくと間違いに気づきやすい。
    void eat() {
        System.out.println("ドッグフードを食べる");
    }

    void bark() {
        System.out.println("ワンワン!");
    }
}

class Cat extends Animal {
    @Override
    void eat() {
        System.out.println("キャットフードを食べる");
    }

    void meow() {
        System.out.println("ニャー!");
    }
}

public class MethodOverrideExample {
    public static void main(String[] args) {
        Animal myAnimal = new Animal();
        myAnimal.eat(); // 何かを食べる

        Dog myDog = new Dog();
        myDog.eat();    // ドッグフードを食べる (オーバーライドされたメソッド)
        myDog.bark();

        Cat myCat = new Cat();
        myCat.eat();    // キャットフードを食べる (オーバーライドされたメソッド)
        myCat.meow();
    }
}

実行結果はこうなるよ。

何かを食べる
ドッグフードを食べる
ワンワン!
キャットフードを食べる
ニャー!

DogクラスとCatクラスのeat()メソッドは、それぞれ独自の振る舞いをしているのがわかるね。@Overrideアノテーションは、コンパイラに「これはオーバーライドするつもりですよ」と伝えるためのものなんだ。これがあることで、もし親クラスに同じ名前のメソッドがなかったり、引数の型が違ったりした場合に、コンパイラがエラーを出してくれるから、ミスを防ぐことができるんだ。

継承の注意点

継承はとても便利な機能だけど、いくつか注意点もあるよ。

  • 多重継承はできない: Javaでは、一つのクラスが直接複数の親クラスを継承することはできないんだ。(例えば、class A extends B, C はできない)これは複雑さを避けるためだけど、代わりにインターフェースという別の仕組みを使って、複数の異なる性質を持たせることができるんだ。これはまた別の機会に詳しく見ていこうね。
  • privateなメンバーは継承されない: 親クラスでprivateと宣言されたフィールドやメソッドは、子クラスから直接アクセスすることはできないんだ。あくまで「そのクラスの中だけで使うもの」だからね。
  • 深くしすぎない: 継承の階層を深くしすぎると、コードが複雑になり、理解しにくくなることがあるよ。適切なバランスを見つけることが大切だ。

まとめ

今回はJavaの継承について学んだね。

  • 継承は、既存のクラスの性質を別のクラスが受け継ぐことで、コードの再利用性を高める仕組み。
  • **親クラス(スーパークラス)子クラス(サブクラス)**の関係で成り立っている。
  • extendsキーワードを使って継承を宣言する。
  • 子クラスのコンストラクタでsuper()を使って親クラスのコンストラクタを呼び出す。
  • オーバーライドによって、親クラスのメソッドを子クラスで独自の振る舞いに変更できる。

継承は、大規模なプログラムを効率的に開発するための非常に重要な考え方だよ。最初は少し難しく感じるかもしれないけど、実際にコードを書いてみて、色々なパターンを試してみると、徐々に理解が深まるはずだ。

次回は、継承と関連の深い**ポリモーフィズム(多様性)**について見ていこう。これもまた、Javaのオブジェクト指向プログラミングの面白さを感じる機能だよ。お楽しみに!

前回の記事: 独自例外クラスを作ってみよう! | ToolDocs


何か質問があれば、いつでも聞いてね!

コメント

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