【JAVA】レッスン5-05:クラスの継承を理解しよう

記事内に商品プロモーションを含む場合があります

この記事で学べる知識:クラスの継承

この記事の練習問題で使用する知識:
基礎構文、制御構造、メソッド、コレクション(レッスン1~4)クラスの定義と使用コンストラクタカプセル化クラスメンバとインスタンスメンバクラスの継承メソッドのオーバーライド抽象クラスインターフェース

<<前のページ JAVA Topへ 次のページ>>

JAVAの文法「クラスの継承」とは

ここではクラスの継承の意味や使い方を学習します。必要ない方はここをクリックして練習問題へ飛びましょう。



クラスの継承は、既存のクラス(親クラスやスーパークラスとも呼ばれます)の機能を引き継ぎ、新しいクラス(子クラス)を作成する際に使用する重要な仕組みです。

これにより重複したコードを減らし、再利用性や保守性の向上が期待できます。

Javaにおいてはこの継承の仕組みを正しく理解することが、効率的でスッキリとしたコードを書くための鍵となります。

【初心者向け】Javaのオブジェクト指向の概念を表した図。特にクラスの継承、抽象クラス、インターフェース、オーバーライド、インスタンス生成を視覚的に分かりやすくまとめている。 本記事では「クラスの継承」を中心に説明している。

クラスの継承とは?

継承とはあるクラスのプロパティ(フィールド)やメソッドを別のクラスに引き継がせる機能です。

例えばAnimalという動物クラスがあったとすると、そのクラスを継承してDogCatといった具体的な動物クラスを作成できます。

これにより共通する特徴や動作を「親クラス」に集約し、特定の動作や属性を「子クラス」で追加する形で、コードが整理されます。

継承の基本構文と使い方

Javaでクラスを継承する際には、extendsキーワードを使用します。

以下が継承の基本構文です。

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

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

上記のように子クラスは「extends 親クラス名」と記述することで、親クラスのフィールドやメソッドを引き継ぐことができます。

また親クラスの機能をそのまま利用できるほか、子クラスで特別な機能を追加することも可能です。

例えばAnimalというクラスからDogクラスを継承する例を考えてみましょう。

class Animal { //親クラス
    void eat() {
        System.out.println("食べています");
    }
}

class Dog extends Animal { //Animalクラスを継承したDogクラス
    void bark() {
        System.out.println("ワンワン!");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog(); //Dog型の変数dogに、生成したインスタンスを代入
        dog.eat(); // 親クラスのメソッド
        dog.bark(); // 子クラスのメソッド
    }
}

このコードではDogクラスがAnimalクラスを継承しているため、DogクラスはAnimalクラスのeatメソッドを使えるようになっています。

継承を使うメリット

継承を使用する主なメリットは以下の通りです。

  1. コードの再利用:一度定義したクラスの機能を複数のクラスで再利用できます。
  2. 保守性の向上:親クラスでメソッドを修正すれば、すべての子クラスに変更が反映されるため、保守が容易になります。
  3. クラスの階層化:共通の機能を親クラスにまとめ、異なる機能を子クラスに持たせることで、論理的なクラス構造が生まれます。

継承の使用例

以下の例ではPersonクラスを親クラスとし、TeacherクラスとStudentクラスを子クラスとして継承しています。

class Person { 親クラス
    //親クラスのフィールド
    String name;
    int age;

    //親クラスのコンストラクタ
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //親クラスのメソッド
    void introduce() {
        System.out.println("名前: " + name + ", 年齢: " + age);
    }
}

class Teacher extends Person { //Personクラスを継承したTeacherクラス
    //Teacherクラスのフィールド
    String subject;

    //Techerクラスのコンストラクタ
    Teacher(String name, int age, String subject) {
        super(name, age); // 親クラスのコンストラクタを呼び出す
        this.subject = subject;
    }

    //Teacherクラスのメソッド
    void teach() {
        System.out.println(subject + "を教えています。");
    }
}

class Student extends Person { //Personクラスを継承したStudentクラス
    //Studentクラスのフィールド
    int studentId;

    //Studentクラスのコンストラクタ
    Student(String name, int age, int studentId) {
        super(name, age);
        this.studentId = studentId;
    }

    //Studentクラスのメソッド
    void study() {
        System.out.println("勉強しています。");
    }
}

public class Main { //メインクラス
    public static void main(String[] args) { //メインメソッド
        Teacher teacher = new Teacher("田中", 30, "数学"); //インスタンス生成
        Student student = new Student("佐藤", 18, 12345); //インスタンス生成

        teacher.introduce(); // 親クラスのメソッド
        teacher.teach();     // 子クラスのメソッド
        student.introduce(); // 親クラスのメソッド
        student.study();     // 子クラスのメソッド
    }
}

この例ではPersonクラスがTeacherクラスとStudentクラスに継承されています。

TeacherクラスとStudentクラスは、Personクラスのintroduceメソッドを使いつつ、それぞれteachstudyといった独自のメソッドを持っています。

このように継承を用いることで、共通の属性と処理を親クラスにまとめ、各クラスごとの固有の処理を追加することができます。

まとめ

クラスの継承はコードの再利用と保守性を高めるための強力な機能です。

Javaではextendsキーワードを用いて既存のクラスを基に新しいクラスを作成し、共通の機能を継承することができます。

継承を適切に活用することでコードの構造をシンプルにし、複雑なシステムの設計がしやすくなります。

今回の解説を参考に、実際に継承を使ったプログラムを作成してみましょう。

JAVA練習問題5-5_車クラスを作り、動作をシミュレーションしよう

親クラス「Vehicle」を作成し、名前を持たせ、動くための基本的なメソッドを定義します。

次に「Car」という子クラスを作成し、親クラス「Vehicle」を継承して、独自の行動を追加します。

このプログラムでは、「Vehicle」の動作と「Car」特有の動作を確認します。

この問題の要件

以下の要件に従ってプログラムを作成して下さい。

  1. 親クラス「Vehicle」を作成し、以下の要素を含むこと:
    • 変数 name:乗り物の名前を格納するための変数。
    • コンストラクタ:引数として名前を受け取り、name変数に設定する。
    • メソッド move():乗り物が動いていることを表すメッセージを出力する。
  2. 子クラス「Car」を作成し、以下の要素を含むこと:
    • 親クラス「Vehicle」を継承すること。
    • コンストラクタ:引数として車の名前を受け取り、親クラスのコンストラクタを呼び出す。
    • メソッド honk():クラクションを鳴らすメッセージを出力する。
  3. メインクラスで以下の操作を行うこと:
    • 車の名前を「プリウス」として「Car」オブジェクトを生成する。
    • move()メソッドを使用して、車が動いていることを確認する。
    • honk()メソッドを使用して、車がクラクションを鳴らす動作を確認する。

ただし、以下のような実行結果となること。

----- ↓出力される結果の例↓ -----

プリウスが動いています。
プリウスがクラクションを鳴らしています。

この問題を解くヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。

1:Vehicleクラスの定義
  □ String型のname変数を宣言
  □ コンストラクタVehicleを定義し、引数nameで初期化
  □ moveメソッドを定義し、”{name}が動いています。”と出力

2:Carクラスの定義
  □ コンストラクタCarを定義し、superで親クラスのコンストラクタを呼び出す
  □ honkメソッドを定義し、”{name}がクラクションを鳴らしています。”と出力

3:Mainクラスの定義
  □ mainメソッドの定義
  □ □ Carクラスのインスタンスcarを作成し、”プリウス”を引数として設定
  □ □ moveメソッドを呼び出し、”{name}が動いています。”と出力
  □ □ honkメソッドを呼び出し、”{name}がクラクションを鳴らしています。”と出力

ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

// 乗り物クラス(親クラス)を定義します。
class Vehicle {
    // 乗り物の名前を格納する変数
    String name;

    // コンストラクタで乗り物の名前を設定します
    /*【穴埋め問題1】
    ここにVehicleクラスのコンストラクタを定義してください。
    コンストラクタでは引数nameを初期化します。
    */
    
    // 乗り物が動くメソッド(親クラスで定義される基本的な動作)
    void move() {
        System.out.println(name + "が動いています。");
    }
}

// 車クラス(子クラス)を定義します。Vehicleクラスを継承します。
class Car extends Vehicle {
    
    /*【穴埋め問題2】
    ここにCarクラスのコンストラクタを定義してください。
    コンストラクタで親クラスのコンストラクタを呼び出し、名前を設定します。
    */

    // 車専用の行動(クラクションを鳴らす)を定義します
    void honk() {
        System.out.println(name + "がクラクションを鳴らしています。");
    }
}

// 実行用クラス
public class Main {
    public static void main(String[] args) {
        // 車オブジェクトを生成し、名前を「プリウス」に設定します
        /*【穴埋め問題3】
        ここにCarクラスのオブジェクトを作成し、変数carに代入するコードを書いてください。
        */

        // 親クラスで定義されたメソッドを呼び出します
        car.move();
        
        // 子クラスで定義されたメソッドを呼び出します
        car.honk();
    }
}

この問題の穴埋めコードは以上です。

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

解答例と解説

この問題の一つの正解例とそのコードの解説を以下に示します。

正解コードの例

例えば以下のようなプログラムが考えられます。

// 乗り物クラス(親クラス)を定義します。
class Vehicle {
    // 乗り物の名前を格納する変数
    String name;

    // コンストラクタで乗り物の名前を設定します
    Vehicle(String name) {
        this.name = name;
    }

    // 乗り物が動くメソッド(親クラスで定義される基本的な動作)
    void move() {
        System.out.println(name + "が動いています。");
    }
}

// 車クラス(子クラス)を定義します。Vehicleクラスを継承します。
class Car extends Vehicle {
    
    // コンストラクタで車の名前を設定します(親クラスのコンストラクタを呼び出します)
    Car(String name) {
        super(name);
    }

    // 車専用の行動(クラクションを鳴らす)を定義します
    void honk() {
        System.out.println(name + "がクラクションを鳴らしています。");
    }
}

// 実行用クラス
public class Main {
    public static void main(String[] args) {
        // 車オブジェクトを生成し、名前を「プリウス」に設定します
        Car car = new Car("プリウス");

        // 親クラスで定義されたメソッドを呼び出します
        car.move();
        
        // 子クラスで定義されたメソッドを呼び出します
        car.honk();
    }
}

正解コードの解説

このコードではJAVAの「クラスの継承」を使って親クラスと子クラスの関係を表現しています。

コードの各ブロックで使用されている文法と、どのような動作を実現しているのかを解説していきます。

親クラスの定義

class Vehicle {
    String name;

    Vehicle(String name) {
        this.name = name;
    }

    void move() {
        System.out.println(name + "が動いています。");
    }
}
  • クラス定義Vehicleクラスは親クラスとして定義されています。このクラスはすべての「乗り物」を表現する基本的な特徴を持ちます。
  • 変数の定義String name;で乗り物の名前を格納する変数nameを定義しています。この変数はクラス内のどこでも使用できるようにクラス全体でアクセス可能です。
  • コンストラクタVehicle(String name)は、Vehicleオブジェクトが生成される際に、名前を初期設定するために使われます。このコンストラクタは、オブジェクトのname変数に引数で渡された名前を代入します。
  • メソッドの定義void move()は、Vehicleクラスの基本的な動作である「動く」機能を表しています。このメソッドを呼び出すと、設定されたnameに基づいて「○○が動いています。」と表示されます。

子クラスの定義と継承

class Car extends Vehicle {
    
    Car(String name) {
        super(name);
    }

    void honk() {
        System.out.println(name + "がクラクションを鳴らしています。");
    }
}
  • 継承の仕組みclass Car extends Vehicleと記述することで、CarクラスはVehicleクラスを継承します。これにより、Vehicleクラスの機能(変数nameとメソッドmove())がCarクラスにも引き継がれます。継承は、共通の動作を持つクラス間でコードを再利用するために使われます。
  • superキーワードCar(String name)の中でsuper(name);としています。superは親クラスのコンストラクタを呼び出すためのキーワードです。これにより、Carオブジェクトが生成されるとVehicleのコンストラクタが実行され、nameが設定されます。
  • 子クラスのメソッドvoid honk()は、車専用の動作として「クラクションを鳴らす」機能を追加しています。honk()メソッドを呼ぶと「○○がクラクションを鳴らしています。」と出力されます。これにより、親クラスVehicleにはない独自の機能が子クラスCarで実現されました。

メインクラスでの実行

public class Main {
    public static void main(String[] args) {
        Car car = new Car("プリウス");
        car.move();
        car.honk();
    }
}
  • オブジェクトの生成Car car = new Car("プリウス");では、Carクラスのオブジェクトを生成し、carという変数に代入しています。このとき、Carのコンストラクタに「プリウス」という名前を渡すことで、nameが「プリウス」に設定されます。
  • 親クラスのメソッド呼び出しcar.move();Vehicleクラスで定義されたmove()メソッドが呼び出され、「プリウスが動いています。」と表示されます。このように、継承によりCarオブジェクトは親クラスVehicleのメソッドを利用できます。
  • 子クラスのメソッド呼び出しcar.honk();Carクラス特有のhonk()メソッドを呼び出し、「プリウスがクラクションを鳴らしています。」と表示されます。子クラスは、親クラスのメソッドに加え、独自の機能も持たせることができるのです。
<<前のページ JAVA Topへ 次のページ>>

この記事への質問・コメント

この問題を作成するにあたりAIを活用しています。

問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。






    JAVAテキスト&問題集へ戻る
    トップページへ戻る