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

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

この記事で学べる知識:抽象クラス

この記事の練習問題を解くために必要な知識:
基礎文法、制御構造、関数、コレクション(レッスン1~4)クラスの定義と使用プライマリコンストラクタセカンダリコンストラクタアクセス修飾子とカプセル化クラスメンバとインスタンスメンバクラスの継承メソッドのオーバーライドクラスの拡張抽象クラスインターフェースデータクラス

<<前のページ Kotlin記事一覧 次のページ>>

Kotlinの文法「抽象クラス」とは

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



抽象クラスは他のクラスが継承するための基盤となるクラス(親クラスとして使用される前提のクラス)です。

インスタンス化することはできず、具象クラス(具体的に実装されたクラス)がこのクラスを継承することで、共通の設計を提供します。

Kotlinではクラスにabstract修飾子を付けて抽象クラスを定義します。

【初心者向け】Kotlinのオブジェクト指向を分かりやすくまとめた概念図。 特にクラスの継承、オーバーライド、抽象クラス、インターフェース、データクラスの関係性を視覚的に理解できるようまとめている。

抽象クラスの基本構文

以下はKotlinでの抽象クラスの基本的な構文です。

abstract class Animal {        // 抽象クラスの定義
    abstract val name: String  // 具体的な値を持たない抽象プロパティの宣言
    abstract fun makeSound()   // 具体的な処理を持たない抽象メソッドの定義

    fun eat() {                // 通常のメソッド
        println("$name is eating.")
    }
}
  • abstract修飾子:抽象プロパティや抽象メソッドにはabstractを付けます。
  • 抽象プロパティとメソッドの特徴:具体的な実装を持たないため、具象クラスで必ずオーバーライドする必要があります。

抽象クラスと具象クラス

抽象クラスを継承する子クラスは「具象クラス」と呼ばれます。

以下は具体的な例です。

class Dog : Animal() {         // Animalクラスを継承するDogクラスの定義
    override val name = "Dog"  // 抽象プロパティをオーバーライド

    override fun makeSound() { // 抽象メソッドをオーバーライド
        println("Woof!")
    }
}

class Cat : Animal() {         // Animalクラスを継承するCatクラスの定義
    override val name = "Cat"  // 抽象プロパティをオーバーライド

    override fun makeSound() { // 抽象メソッドをオーバーライド
        println("Meow!")
    }
}

ここでDogCatAnimalクラスを継承し、それぞれのnameプロパティとmakeSoundメソッドを実装しています。

fun main() {
    val dog = Dog()
    dog.makeSound()  // Woof!
    dog.eat()        // Dog is eating.

    val cat = Cat()
    cat.makeSound()  // Meow!
    cat.eat()        // Cat is eating.
}

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

Woof!
Dog is eating.
Meow!
Cat is eating.

抽象プロパティと抽象メソッド

抽象クラスは具体的な実装が存在しないプロパティやメソッドを定義できます。

  1. 抽象プロパティnameのように値が決まっていない変数を具象クラスで実装します。
  2. 抽象メソッドmakeSoundのように動作を具体的に具象クラスで実装します。

また共通の振る舞い(例:eatメソッド)は抽象クラス内で通常のメソッドとして実装可能です。

まとめ

抽象クラスは共通のプロパティやメソッドを定義する基盤として役立ちます。

具象クラスは抽象クラスを継承し、具体的な振る舞いを実装します。

これによりコードの再利用性と拡張性が向上します。

抽象クラスの練習問題

図書館の書籍情報を管理するシステムを作成しましょう。

この問題の要件

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

  • 書籍の情報(タイトルと著者)を保持する抽象クラス Book を定義すること。
  • Book クラスを継承した LibraryBook クラスを定義し、書籍情報を表示するメソッドを実装すること。
  • 複数の LibraryBook オブジェクトを管理する Library クラスを定義すること。
  • Library クラスには、書籍を追加するメソッド、すべての書籍情報を表示するメソッド、指定された著者の書籍を検索して表示するメソッドを実装すること。
  • メイン関数で Library クラスのインスタンスを作成し、複数の書籍を追加して、全書籍情報と特定の著者の書籍情報を表示すること。

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

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

書籍「吾輩は猫である」が追加されました。
書籍「こころ」が追加されました。
書籍「羅生門」が追加されました。
すべての書籍情報:
タイトル: 吾輩は猫である, 著者: 夏目漱石
タイトル: こころ, 著者: 夏目漱石
タイトル: 羅生門, 著者: 芥川龍之介
著者「夏目漱石」の書籍情報:
タイトル: 吾輩は猫である, 著者: 夏目漱石
タイトル: こころ, 著者: 夏目漱石

この問題を解くヒント

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

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

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

1:抽象クラスBookを定義
  □ 抽象プロパティtitleとauthorを定義
  □ 抽象メソッドdisplayInfoを定義
2:クラスLibraryBookを定義
  □ クラスBookを継承
  □ 抽象メソッドdisplayInfoをオーバーライド
  □ □ 文字列テンプレートを使用してタイトルと著者を出力
3:クラスLibraryを定義
  □ プライベートプロパティbooksをmutableListとして初期化
  □ 関数addBookを定義
  □ □ 書籍をリストbooksに追加
  □ □ 文字列テンプレートで書籍追加のメッセージを出力
  □ 関数displayAllBooksを定義
  □ □ 「すべての書籍情報:」と出力
  □ □ forループでリストbooks内のすべての書籍情報を表示
  □ 関数searchByAuthorを定義
  □ □ 文字列テンプレートで著者検索のメッセージを出力
  □ □ forループでリストbooks内の著者一致する書籍情報を表示
4:メイン関数を定義
  □ クラスLibraryのインスタンスlibraryを作成
  □ クラスLibraryBookのインスタンスbook1, book2, book3を作成
  □ 関数addBookを使用して書籍をlibraryに追加
  □ 関数displayAllBooksを使用してすべての書籍情報を表示
  □ 関数searchByAuthorを使用して著者名「夏目漱石」の書籍情報を表示

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

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

// 抽象クラス Book を定義
abstract class Book(val title: String, val author: String) {
    // 抽象メソッド
    abstract fun displayInfo()
}

// Book クラスを継承した LibraryBook クラスを定義
class LibraryBook(title: String, author: String) : Book(title, author) {
    // 抽象メソッドをオーバーライドして実装
    /*【穴埋め問題1】
    ここに抽象メソッド displayInfo をオーバーライドして、書籍のタイトルと著者を表示するコードを書いてください。
    */
}

// Library クラスを定義
class Library {
    // 書籍リストを保持するプロパティ
    private val books: MutableList<LibraryBook> = mutableListOf()

    // 書籍を追加するメソッド
    fun addBook(book: LibraryBook) {
        books.add(book)
        println("書籍「${book.title}」が追加されました。")
    }

    // すべての書籍情報を表示するメソッド
    fun displayAllBooks() {
        println("すべての書籍情報:")
        /*【穴埋め問題2】
        ここにfor文を使って、すべての書籍の情報を表示するコードを書いてください。displayInfo メソッドを使用します。
        */
    }

    // 指定された著者の書籍を表示するメソッド
    /*【穴埋め問題3】
    ここに、著者名で書籍を検索し、その著者の書籍情報を表示するコードを書いてください。
    */
}

// メイン関数
fun main() {
    // Library のインスタンスを作成
    val library = Library()

    // 書籍を追加
    val book1 = LibraryBook("吾輩は猫である", "夏目漱石")
    val book2 = LibraryBook("こころ", "夏目漱石")
    val book3 = LibraryBook("羅生門", "芥川龍之介")

    library.addBook(book1)
    library.addBook(book2)
    library.addBook(book3)

    // すべての書籍情報を表示
    library.displayAllBooks()

    // 特定の著者の書籍を検索
    library.searchByAuthor("夏目漱石")
}

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

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

練習問題の解答と解説

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

正解コードの例

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

// 抽象クラス Book を定義
abstract class Book(val title: String, val author: String) {
    // 抽象メソッド
    abstract fun displayInfo()
}

// Book クラスを継承した LibraryBook クラスを定義
class LibraryBook(title: String, author: String) : Book(title, author) {
    // 抽象メソッドをオーバーライドして実装
    override fun displayInfo() {
        println("タイトル: $title, 著者: $author")
    }
}

// Library クラスを定義
class Library {
    // 書籍リストを保持するプロパティ
    private val books: MutableList<LibraryBook> = mutableListOf()

    // 書籍を追加するメソッド
    fun addBook(book: LibraryBook) {
        books.add(book)
        println("書籍「${book.title}」が追加されました。")
    }

    // すべての書籍情報を表示するメソッド
    fun displayAllBooks() {
        println("すべての書籍情報:")
        for (book in books) {
            book.displayInfo()
        }
    }

    // 指定された著者の書籍を表示するメソッド
    fun searchByAuthor(author: String) {
        println("著者「$author」の書籍情報:")
        for (book in books) {
            if (book.author == author) {
                book.displayInfo()
            }
        }
    }
}

// メイン関数
fun main() {
    // Library のインスタンスを作成
    val library = Library()

    // 書籍を追加
    val book1 = LibraryBook("吾輩は猫である", "夏目漱石")
    val book2 = LibraryBook("こころ", "夏目漱石")
    val book3 = LibraryBook("羅生門", "芥川龍之介")

    library.addBook(book1)
    library.addBook(book2)
    library.addBook(book3)

    // すべての書籍情報を表示
    library.displayAllBooks()

    // 特定の著者の書籍を検索
    library.searchByAuthor("夏目漱石")
}

正解コードの解説

今回のコードでは抽象クラスを用いたクラス設計の基本的な使い方を学びます。

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

抽象クラス Book

// 抽象クラス Book を定義
abstract class Book(val title: String, val author: String) {
    // 抽象メソッド
    abstract fun displayInfo()
}
  • 抽象クラスabstract class キーワードを使って抽象クラスを定義します。抽象クラスはテンプレートとして使用され、具体的なクラスで継承されることを目的としています。
  • プロパティtitleauthor は書籍のタイトルと著者を表すプロパティです。
  • 抽象メソッドabstract fun displayInfo() は抽象メソッドです。この例ではdisplayInfo メソッドが具体的なクラスで実装される必要があります。

クラス LibraryBook

// Book クラスを継承した LibraryBook クラスを定義
class LibraryBook(title: String, author: String) : Book(title, author) {
    // 抽象メソッドをオーバーライドして実装
    override fun displayInfo() {
        println("タイトル: $title, 著者: $author")
    }
}
  • 継承class LibraryBook : Book(title, author)Book クラスを継承しています。これによりBook クラスのプロパティとメソッドを使用できます。
  • コンストラクタLibraryBook クラスのコンストラクタは親クラスである Book のコンストラクタを呼び出しています。
  • メソッドのオーバーライドoverride fun displayInfo()Book クラスの抽象メソッド displayInfo をオーバーライドして具体的に実装しています。このメソッドは書籍のタイトルと著者を表示します。

クラス Library

// Library クラスを定義
class Library {
    // 書籍リストを保持するプロパティ
    private val books: MutableList<LibraryBook> = mutableListOf()

    // 書籍を追加するメソッド
    fun addBook(book: LibraryBook) {
        books.add(book)
        println("書籍「${book.title}」が追加されました。")
    }

    // すべての書籍情報を表示するメソッド
    fun displayAllBooks() {
        println("すべての書籍情報:")
        for (book in books) {
            book.displayInfo()
        }
    }

    // 指定された著者の書籍を表示するメソッド
    fun searchByAuthor(author: String) {
        println("著者「$author」の書籍情報:")
        for (book in books) {
            if (book.author == author) {
                book.displayInfo()
            }
        }
    }
}
  • プロパティbooksMutableList<LibraryBook> 型のプロパティで、追加された書籍を保持します。
  • メソッド addBook:このメソッドは LibraryBook オブジェクトを books リストに追加し、追加されたことを表示します。
  • メソッド displayAllBooks:このメソッドは books リストに含まれるすべての書籍情報を表示します。
  • メソッド searchByAuthor:このメソッドは指定された著者の書籍情報を検索し、該当する書籍を表示します。

メイン関数 main

// メイン関数
fun main() {
    // Library のインスタンスを作成
    val library = Library()

    // 書籍を追加
    val book1 = LibraryBook("吾輩は猫である", "夏目漱石")
    val book2 = LibraryBook("こころ", "夏目漱石")
    val book3 = LibraryBook("羅生門", "芥川龍之介")

    library.addBook(book1)
    library.addBook(book2)
    library.addBook(book3)

    // すべての書籍情報を表示
    library.displayAllBooks()

    // 特定の著者の書籍を検索
    library.searchByAuthor("夏目漱石")
}
  • インスタンスの作成Library クラスのインスタンス library を作成します。
  • 書籍の追加:3つの LibraryBook オブジェクトを作成し、それぞれの書籍を library に追加します。
  • 書籍情報の表示library.displayAllBooks() メソッドを呼び出して、すべての書籍情報を表示します。
  • 著者の書籍を検索library.searchByAuthor("夏目漱石") メソッドを呼び出して、特定の著者の書籍情報を表示します。

まとめ

今回のコードではKotlinの抽象クラスを使用して書籍情報を管理するシステムを構築しました。

抽象クラス Book とその具体的な実装である LibraryBook クラス、そして書籍を管理する Library クラスを組み合わせて、柔軟で拡張可能なシステムを作成しました。

メイン関数では実際に書籍を追加し、情報を表示する処理を行っています。

この例を通じてKotlinのクラスと継承、メソッドのオーバーライドの基本を学習することができます。

<<前のページ Kotlin記事一覧 次のページ>>

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

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

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






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