【Java】レッスン5-7:抽象クラスを理解しよう

ながみえ

一つ前のページではメソッドのオーバーライドについて学習しました。

今回は 抽象クラス について見ていきましょう。

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のオブジェクト指向では、クラスを設計する際に「共通の仕様は決めるが、具体的な処理は後で決める」という場面があります。

そんなときに活躍するのが抽象クラスです。

抽象クラスは、処理を持たない抽象メソッドを定義し、継承先のクラスに実装を任せることで、コードの設計を柔軟かつ拡張しやすくします。

この知識を身につけることで、共通の枠組みを持ちながらクラスごとに異なる振る舞いを実装できるようになり、大規模開発や複雑な設計にも対応しやすくなります。

この記事では、抽象クラスと抽象メソッドの基本から、具体的なコード例、活用のメリットまでわかりやすく解説します。

ぜひ最後まで読み進めて、オブジェクト指向設計の幅を広げていきましょう。

抽象クラスとは何か?|抽象メソッドで実装を子クラスに任せる方法

抽象クラス は「インスタンス生成できないクラス」であり、他の子クラスに継承されることを前提としています。

通常のクラスと同様に変数やメソッドを持つことができますが、特定のメソッドにおいて実装を持たない 抽象メソッド を含めることができる点が特徴です。

抽象クラスを使うことで共通の基盤を提供し、複数の子クラスでの個別の実装を促すことができます。

抽象クラスは abstractキーワード を用いて宣言し、抽象メソッドも同様に abstractキーワードを使って定義します。

abstract class 抽象クラス名 {
    abstract void sound(); // 抽象メソッド(具体的な処理内容を持たない)
}

抽象クラスは抽象メソッドを持ちますが、その抽象メソッドには具体的な処理内容がありません。

抽象クラスを継承した子クラスから、オーバーライドされることを前提としています。

そして子クラスは、親クラスに抽象メソッドがある場合は必ずそれを実装しないといけません。

抽象クラスの実装例|サンプルコードで動作を確認しよう

抽象クラスを使うと共通の動作を持ちながらも、具体的な部分を子クラスで自由に定義できます。

次にDogCat という2つのクラスが Animal クラスを継承し、各クラスで異なる sound() メソッドを実装する例を見てみましょう。

abstract class Animal {		// Animalという名前の抽象クラスの定義
    abstract void sound();	// 抽象メソッド(具体的な実装を持たない)
    void sleep() {			// 通常のメソッドも定義可能
        System.out.println("Sleeping...");
    }
}
class Dog extends Animal {	// Dogクラス(Animalを継承)
    @Override				// Overrideアノテーション
    void sound() {			// 抽象メソッドをオーバーライドして具体的な実装
        System.out.println("Woof!");
    }
}
class Cat extends Animal {	// Catクラス(Animalを継承)
    @Override				// Overrideアノテーション
    void sound() {			// 抽象メソッドをオーバーライド
        System.out.println("Meow!");
    }
}
public class Main {			// メインクラス
    public static void main(String[] args) {
        Dog dog = new Dog();	// インスタンス生成
        Cat cat = new Cat();	// インスタンス生成

        dog.sound();	// 出力: Woof!
        dog.sleep();	// 出力: Sleeping...

        cat.sound();	// 出力: Meow!
        cat.sleep();	// 出力: Sleeping...
    }
}

この例ではDog クラスと Cat クラスが Animal クラスを継承し、それぞれのクラスで sound() メソッドを独自に実装しています。

Main クラスの main メソッド内で DogCat をそれぞれインスタンス化して sound() を呼び出すと、各クラスの実装に従った出力が得られます。

またsleep() メソッドは Animal クラスで既に実装されているため、子クラスで再定義する必要がありません。

まとめ|共通仕様と柔軟な実装を両立する抽象クラス

今回学んだ抽象クラスは、共通の仕様を定義しながら、詳細な処理は子クラスに任せるという設計手法でした。

抽象メソッドを用いることで、複数のクラス間で統一されたインターフェースを持ちつつ、それぞれ異なる実装を行えるようになります。

これにより、コードの再利用性や保守性が向上し、柔軟な拡張が可能になります。

ぜひこの知識を活かして、自分なりのクラス設計に取り入れてみてください。

練習問題|抽象クラスで鳴き声を出力するプログラムを作成しよう

動物の種類によって異なる鳴き声を表示するプログラムを作成しましょう。

このプログラムでは動物の基本的なクラスとして抽象クラスを使い、それぞれの動物(犬と猫)に特有の鳴き声を実装します。

また共通の動作としてすべての動物が「寝ている」という動作も表示させましょう。

この問題の要件

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

  1. 抽象クラス Animal を定義し、以下の内容を持たせること。
    • makeSound() という抽象メソッドを宣言すること(戻り値は void)。
    • sleep() という具象メソッドを実装し、「動物は寝ています…」と表示すること。
  2. Animal クラスを継承する Dog クラスを定義し、以下を実装すること。
    • makeSound() メソッドで「ワンワン!」と出力すること。
  3. Animal クラスを継承する Cat クラスを定義し、以下を実装すること。
    • makeSound() メソッドで「ニャーニャー!」と出力すること。
  4. Main クラスの main メソッドで、以下を実行すること。
    • Dog クラスのインスタンスを生成し、その makeSound() メソッドと sleep() メソッドを呼び出すこと。
    • Cat クラスのインスタンスを生成し、その makeSound() メソッドと sleep() メソッドを呼び出すこと。

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

ワンワン!
ニャーニャー!
動物は寝ています...
動物は寝ています...

この問題を解くヒント

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

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

正解のコードは上から順に以下のような構成となっています。
(※下記の□はコード内のインデントを表しています)

1:抽象クラスAnimalの定義
  □ 抽象メソッドmakeSoundの宣言
  □ 具象メソッドsleepの定義
    □ 「動物は寝ています…」と出力

2:Dogクラスの定義
  □ Animalクラスを継承
  □ makeSoundメソッドをオーバーライドし、「ワンワン!」と出力

3:Catクラスの定義
  □ Animalクラスを継承
  □ makeSoundメソッドをオーバーライドし、「ニャーニャー!」と出力

4:Mainクラスの定義
  □ mainメソッドの定義
  □ □ Dogクラスのインスタンスを生成し、変数dogに代入
  □ □ Catクラスのインスタンスを生成し、変数catに代入
  □ □ dogのmakeSoundメソッドを呼び出し、鳴き声を出力
  □ □ catのmakeSoundメソッドを呼び出し、鳴き声を出力
  □ □ dogのsleepメソッドを呼び出し、「動物は寝ています…」を出力
  □ □ catのsleepメソッドを呼び出し、「動物は寝ています…」を出力

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

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

// 動物の種類を表す抽象クラス
abstract class Animal {
    /*【穴埋め問題1】
    ここに抽象メソッドmakeSoundを宣言してください。
    */

    // 共通の動作を定義するメソッド
    public void sleep() {
        System.out.println("動物は寝ています...");
    }
}

// 犬のクラス (Animalクラスを継承して実装)
class Dog extends Animal {
    /*【穴埋め問題2】
    ここにmakeSoundメソッドをオーバーライドし、「ワンワン!」と出力するコードを書いてください。
    */
}

// 猫のクラス (Animalクラスを継承して実装)
class Cat extends Animal {
    /*【穴埋め問題3】
    ここにmakeSoundメソッドをオーバーライドし、「ニャーニャー!」と出力するコードを書いてください。
    */
}

public class Main {
    public static void main(String[] args) {
        // DogとCatのインスタンスを作成し、動作を確認する
        Animal dog = new Dog();  // DogはAnimalのサブクラス
        Animal cat = new Cat();  // CatはAnimalのサブクラス

        // 各動物の鳴き声を出す (抽象メソッドの実装部分が呼び出される)
        dog.makeSound();
        cat.makeSound();

        // sleepメソッドはAnimalクラスで実装されているので、直接使用できる
        dog.sleep();
        cat.sleep();
    }
}

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

練習問題の解答と解説

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

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

Q
正解コード
// 動物の種類を表す抽象クラス
abstract class Animal {
    // 抽象メソッド: 具体的な動物クラスで実装する必要がある
    public abstract void makeSound();

    // 共通の動作を定義するメソッド
    public void sleep() {
        System.out.println("動物は寝ています...");
    }
}

// 犬のクラス (Animalクラスを継承して実装)
class Dog extends Animal {
    // 抽象メソッドを具体的に実装する
    @Override
    public void makeSound() {
        System.out.println("ワンワン!");
    }
}

// 猫のクラス (Animalクラスを継承して実装)
class Cat extends Animal {
    // 抽象メソッドを具体的に実装する
    @Override
    public void makeSound() {
        System.out.println("ニャーニャー!");
    }
}

public class Main {
    public static void main(String[] args) {
        // DogとCatのインスタンスを作成し、動作を確認する
        Animal dog = new Dog();  // DogはAnimalのサブクラス
        Animal cat = new Cat();  // CatはAnimalのサブクラス

        // 各動物の鳴き声を出す (抽象メソッドの実装部分が呼び出される)
        dog.makeSound();
        cat.makeSound();

        // sleepメソッドはAnimalクラスで実装されているので、直接使用できる
        dog.sleep();
        cat.sleep();
    }
}
Q
正解コードの解説

コードをブロックごとに分割して解説します。

抽象クラスと抽象メソッド

abstract class Animal {
    public abstract void makeSound();

    public void sleep() {
        System.out.println("動物は寝ています...");
    }
}
  • 抽象クラス(abstract class Animal
    抽象クラス Animal は「動物」を表す基本クラスであり、特定の動作を子クラスで実装するための基盤です。abstract キーワードによりこのクラスは直接インスタンス化できず、他のクラスが継承するためだけに存在します。
  • 抽象メソッド(public abstract void makeSound();
    makeSound は抽象メソッドです。抽象メソッドは具体的な処理を持たず、子クラスで必ず実装されなければなりません。このメソッドを通して動物ごとに異なる鳴き声を定義できます。
  • 具象メソッド(public void sleep()
    sleep メソッドは具体的な処理を持つメソッドで、すべての動物が同じ動作(「寝ています…」のメッセージ出力)を行います。抽象クラス内に具象メソッドを定義することで共通の動作を各子クラスに引き継げます。

犬のクラスの定義

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("ワンワン!");
    }
}
  • 継承(class Dog extends Animal
    Dog クラスは Animal クラスを継承しているため、Animal クラスのすべてのメソッドやプロパティを利用できます。ここでは makeSound メソッドを具体的に実装する必要があるため@Override アノテーションを使用します。
  • メソッドのオーバーライド
    makeSound メソッドをオーバーライドして犬の鳴き声「ワンワン!」を出力するようにしています。これにより、犬特有の動作を表現できます。

猫のクラスの定義

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("ニャーニャー!");
    }
}
  • 継承とメソッドのオーバーライド
    Cat クラスも Animal クラスを継承しており、makeSound メソッドをオーバーライドして猫の鳴き声「ニャーニャー!」を出力します。これにより、異なる動物がそれぞれの鳴き声を持つことができます。

Mainクラスとメインメソッド

  • インスタンス化とポリモーフィズム
    Animal 型の変数 dogDog クラスのインスタンスを、catCat クラスのインスタンスを代入しています。Animal 型を通して異なるクラスのオブジェクトを扱うことで、ポリモーフィズムを活用しています。これにより、makeSound メソッドは dog では「ワンワン!」、cat では「ニャーニャー!」と異なる動作を行います。
  • メソッドの呼び出し
    makeSound メソッドを呼び出すと、それぞれの鳴き声が出力されます。また、sleep メソッドは Animal クラスで定義されているため、すべての Animal クラスを継承したインスタンスが同じ「動物は寝ています…」のメッセージを表示します。
もっと分かりやすい学習サイトにするために

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

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

<<前のページ

学習記事一覧

次のページ>>

記事URLをコピーしました