Kotlin練習問題4-5:抽象クラスを理解しよう

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

Kotlinの初心者向け問題4-5:抽象クラスを理解しよう

この問題を解くために必要な知識:

【レベル1~3の知識】
基礎文法、四則演算と論理演算、分岐処理(if、if~else、when)、繰り返し処理(for、while、do~while)、配列、Null安全、List(MutableList、ArrayList)、関数の基本、関数の戻り値、関数のオーバーロード、クラスの基本とインスタンス、コンストラクタ、プロパティ、クラスの継承、クラスの拡張、コレクションの基礎、Set(HashSet、MutableSet、TreeSet)、Map(HashMap、MutableMap、TreeMap)

【レベル4の知識】
オブジェクトの基礎、インスタンスメソッド、カプセル化クラスメンバ、抽象クラス、インターフェースデータクラス

<<前の問題 問題集Top 次の問題>>

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

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



プログラミングにおいて、抽象クラスはオブジェクト指向の基本概念の一つです。

Kotlinでも抽象クラスを利用することで、コードの柔軟性を高め、効率的なプログラム設計が可能になります。

この記事では、抽象クラスの役割やその定義方法、使用例について詳しく説明します。

抽象クラスとは?

抽象クラスは、クラスの基本的な枠組みを定義するもので、直接インスタンス化することができないクラスです。

抽象クラスは「テンプレート」として機能し、他のクラスに共通のプロパティやメソッドを提供しますが、具体的な動作はそのクラスを継承したサブクラスで実装する必要があります。

例えば、「動物」を表すクラスを考えた場合、動物は一般的な概念であり、具体的な種類(犬や猫など)の動物を表すサブクラスでその行動を定義することができます。

動物そのものをインスタンス化することはできませんが、動物を継承したクラスがその機能を実装します。

抽象クラスの定義

Kotlinで抽象クラスを定義するには、abstractキーワードを使用します。

抽象クラス内では、抽象メソッド(具体的な実装を持たないメソッド)も定義でき、そのメソッドはサブクラスで必ずオーバーライドされる必要があります。

以下は抽象クラスの基本的な定義例です。

// 抽象クラスの定義
abstract class Animal {
    // 抽象メソッド
    abstract fun makeSound()
    
    // 通常のメソッド
    fun sleep() {
        println("The animal is sleeping")
    }
}

// サブクラスで抽象メソッドを実装
class Dog : Animal() {
    override fun makeSound() {
        println("Bark")
    }
}

class Cat : Animal() {
    override fun makeSound() {
        println("Meow")
    }
}

の例では、Animalクラスが抽象クラスとして定義されています。

Animalには抽象メソッドmakeSound()があり、これを継承するDogCatクラスで実装しています。

また、抽象クラス内にはsleep()のような通常のメソッドも含まれており、これは全てのサブクラスで共有されます。

抽象クラスの使用例

抽象クラスは、サブクラスに共通の動作を持たせつつ、個別の実装を強制する場面で便利です。

例えば、以下のように具体的なサブクラスを利用して抽象クラスを活用します。

fun main() {
    val dog = Dog()
    val cat = Cat()
    
    dog.makeSound() // 出力: Bark
    dog.sleep()     // 出力: The animal is sleeping
    
    cat.makeSound() // 出力: Meow
    cat.sleep()     // 出力: The animal is sleeping
}

このコードでは、DogCatクラスはmakeSound()メソッドをそれぞれ独自に実装していますが、sleep()メソッドは抽象クラスAnimalで定義されたものをそのまま利用しています。

このようにして、共通する機能を一元管理しつつ、個別の動作も柔軟に定義できます。

抽象クラスとインターフェースの違い

抽象クラスは、インターフェースと混同されがちですが、いくつかの点で異なります。

最も大きな違いは、抽象クラスは状態(プロパティ)を持つことができるのに対し、インターフェースは状態を持つことができません。

Kotlinでは多重継承が許可されていないため、クラスは1つの抽象クラスしか継承できませんが、インターフェースは複数実装できます。

// インターフェースの例
interface Flyable {
    fun fly()
}

// 抽象クラスとインターフェースの両方を利用
class Bird : Animal(), Flyable {
    override fun makeSound() {
        println("Chirp")
    }
    
    override fun fly() {
        println("The bird is flying")
    }
}

上記の例では、BirdクラスがAnimal抽象クラスを継承しつつ、Flyableインターフェースも実装しています。

このように、抽象クラスとインターフェースを組み合わせることで、より柔軟な設計が可能になります。

まとめ

Kotlinにおける抽象クラスは、共通の動作を定義し、サブクラスでの具体的な実装を強制するための強力な手段です。

抽象クラスを活用することで、コードの再利用性を高め、プログラム全体の構造をシンプルに保つことができます。

インターフェースとの違いを理解し、適切に使い分けることで、より洗練されたプログラムが作成できるでしょう。




Kotlin練習問題4-5_図書館の書籍情報を管理するプログラムを作ろう

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

この問題の要件

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

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

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

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

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

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

この問題を解くヒント

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

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

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

1.抽象クラス Book の定義
 1-1. コンストラクタで title と author プロパティを初期化
 1-2. 抽象メソッド displayInfo の宣言
2.クラス LibraryBook の定義
 2-1. Book クラスの継承
 2-2. コンストラクタで title と author プロパティを初期化
 2-3. メソッド displayInfo のオーバーライドと実装
3.クラス Library の定義
 3-1. プロパティ books の宣言と初期化
 3-2. メソッド addBook の定義
  3-2-1. books リストに書籍を追加
  3-2-2. 書籍が追加されたことを表示
 3-3. メソッド displayAllBooks の定義
  3-3-1. すべての書籍情報を表示
 3-4. メソッド searchByAuthor の定義
  3-4-1. 指定された著者の書籍情報を表示
4.メイン関数 main の定義
 4-1. Library クラスのインスタンスを作成
 4-2. LibraryBook クラスのインスタンスを作成
 4-3. 書籍をライブラリに追加
 4-4. すべての書籍情報を表示
 4-5. 特定の著者の書籍情報を検索

ヒント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("夏目漱石")
}

********************

正解コードの解説

この解説では、指定されたKotlinコードの各部分を初心者向けに説明します。特に重要な「抽象クラス」について詳しく解説します。

1. 抽象クラス Book

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

2. クラス 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 をオーバーライドして具体的に実装しています。このメソッドは書籍のタイトルと著者を表示します。

3. クラス 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:このメソッドは指定された著者の書籍情報を検索し、該当する書籍を表示します。

4. メイン関数 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のクラスと継承、メソッドのオーバーライドの基本を理解することができます。

<<前の問題 問題集Top 次の問題>>

この問題への質問・コメント

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

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






    Kotlin練習問題集へ戻る
    トップページへ戻る