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

ながみえ

一つ前のページではクラスメンバとインスタンスメンバについて学習しました。

今回は クラスの継承 について見ていきましょう。

Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:メソッド編
Lesson4:コレクション編
Lesson5:クラス

 ・Lesson5-1:クラスの基本を確認しよう
 ・Lesson5-2:コンストラクタを理解しよう
 ・Lesson5-3:カプセル化を理解しよう
 ・Lesson5-4:クラスメンバとインスタンスメンバを理解しよう
 ・Lesson5-5:クラスの継承を理解しよう ◁今回はココ
 ・Lesson5-6:メソッドのオーバーライドを理解しよう
 ・Lesson5-7:抽象クラスを理解しよう
 ・Lesson5-8:インターフェースを理解しよう
 ・確認問題5-☆1:モンスター捕獲ゲームを作ろう
 ・確認問題5-☆2:モンスターとのバトルゲームを作ろう
 ・確認問題5-☆3:マルバツゲーム(Tic-Tac-Toe)を作成しよう

<<前のページ

学習記事一覧

次のページ>>

継承入門|親クラスの機能を受け継ぐ仕組みと使い方

Javaのクラス継承は、既存のクラス(親クラス)の機能やデータを新しいクラス(子クラス)に引き継ぐことで、コードの再利用性や拡張性を高める仕組みです。

extendsキーワードを使うことで、重複した処理を減らし、共通部分をまとめた効率的なプログラム設計が可能になります。

これを理解すれば、複雑なアプリケーションでも構造を整理しやすくなり、保守性の高いコードを書く力が身につきます。

この記事では、初心者でもわかるように基本構文から実用例まで丁寧に解説しますので、一緒に継承の基礎をマスターしていきましょう。

継承とは何か?|extendsによる機能の引き継ぎと基礎知識

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

例えば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() {			// Dogクラスの独自メソッドの定義
        System.out.println("ワンワン!");
    }
}
public class Main {			 // メインクラス
    public static void main(String[] args) {
        Dog dog = new Dog(); // Dogクラスのインスタンス生成
        dog.eat();			 // 親クラスのメソッド呼び出し
        dog.bark();			 // 子クラスのメソッド呼び出し
    }
}

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

もう少し複雑な例も見てみましょう。

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

また、子クラスのコンストラクタから親クラスのコンストラクタを呼び出すために superキーワード を使用しています。

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クラスの定義
    String subject;				// Teacherクラスの独自フィールド
    Teacher(String name, int age, String subject) {	// Techerクラスのコンストラクタ
        super(name, age);		// 親クラスのコンストラクタを呼び出す
        this.subject = subject;
    }
    void teach() {	// Teacherクラスの独自メソッド
        System.out.println(subject + "を教えています。");
    }
}

class Student extends Person {		// Personクラスを継承したStudentクラスの定義
    Student(String name, int age) { // Studentクラスのコンストラクタ
        super(name, age);			// 親クラスのコンストラクタを呼び出す
    }
    void study() {	// Studentクラスの独自メソッド
        System.out.println("勉強しています。");
    }
}

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

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

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

このコードを実行すると以下のように出力されます。

名前: 田中, 年齢: 30
数学を教えています。
名前: 佐藤, 年齢: 18
勉強しています。

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

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

まとめ|継承でコードの再利用性と拡張性を高めよう

今回学んだクラスの継承では、親クラスから機能や属性を受け継ぎ、子クラスで固有の機能を追加する方法を理解しました。

この知識により、共通処理をまとめた効率的なコード設計が可能になり、重複の少ない保守性の高いプログラムが書けるようになります。

継承はオブジェクト指向の重要な柱のひとつです。ぜひ今回の知識を活かして、より柔軟で拡張性のあるクラス設計に挑戦してみましょう。

練習問題|継承を使って車の動作を再現するクラスを作ろう

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

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

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

この問題の要件

以下の要件に従ってコードを完成させてください。

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

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

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

この問題を解くヒント

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

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

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

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

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

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

Q
ヒント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();
    }
}

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

練習問題の解答と解説

この問題の正解コードとその解説は以下の通りです。

クリックして開いて確認してください。

Q
正解コード
// 乗り物クラス(親クラス)を定義します。
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();
    }
}
Q
正解コードの解説

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

親クラスの定義

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.honk();Carクラス特有のhonk()メソッドを呼び出し、「プリウスがクラクションを鳴らしています。」と表示されます。子クラスは、親クラスのメソッドに加え、独自の機能も持たせることができるのです。
  • オブジェクトの生成Car car = new Car("プリウス");では、Carクラスのオブジェクトを生成し、carという変数に代入しています。このとき、Carのコンストラクタに「プリウス」という名前を渡すことで、nameが「プリウス」に設定されます。
  • 親クラスのメソッド呼び出しcar.move();Vehicleクラスで定義されたmove()メソッドが呼び出され、「プリウスが動いています。」と表示されます。このように、継承によりCarオブジェクトは親クラスVehicleのメソッドを利用できます。
もっと分かりやすい学習サイトにするために

この記事を読んで「ここが分かりにくかった」「ここが難しかった」等の意見を募集しています。

世界一わかりやすいJava学習サイトにするため、ぜひ 問い合わせフォーム からご意見下さい。

<<前のページ

学習記事一覧

次のページ>>

記事URLをコピーしました