【Kotlin】レッスン5-2:プライマリコンストラクタの基本とinitブロックの使い方を学ぼう

ながみえ

一つ前のページではクラスの基本について学習しました。

今回は プライマリコンストラクタ について見ていきましょう。

Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:関数編
Lesson4:コレクション編
Lesson5:オブジェクト指向編

 ・Lesson5-1:クラスの基本を理解しよう
 ・Lesson5-2:プライマリコンストラクタを理解しよう ◁今回はココ
 ・Lesson5-3:セカンダリコンストラクタを理解しよう
 ・Lesson5-4:ふたつのコンストラクタを使いこなそう
 ・Lesson5-5:アクセス修飾子とカプセル化を理解しよう
 ・Lesson5-6:静的メンバとインスタンスメンバを理解しよう
 ・Lesson5-7:クラスの継承を理解しよう
 ・Lesson5-8:メソッドのオーバーライドを理解しよう
 ・Lesson5-9:クラスを拡張しよう
 ・Lesson5-10:抽象クラスを理解しよう
 ・Lesson5-11:インターフェースを理解しよう
 ・Lesson5-12:データクラスを理解しよう
 ・確認問題5-☆1:モンスター捕獲ゲームを作ろう
 ・確認問題5-☆2:マルバツゲームを作ろう
 ・確認問題5-☆3:石取りゲームを作ろう

<<前のページ

学習記事一覧

次のページ>>

プライマリコンストラクタとは何か?

プログラムにおいて「クラス」は、データと処理をひとまとめにするための仕組みです。

その中で「コンストラクタ」とは、クラスのインスタンス(オブジェクト)を作成するときに最初に呼び出される特別な関数のことを指します。

Kotlinではクラスを初期化するために「プライマリコンストラクタ」と「セカンダリコンストラクタ」の2種類が提供されています。

コンストラクタを使うと、例えば次のことが可能です:

  • クラスのインスタンスを作成する際に、必要なデータを渡す。
  • 初期化処理を簡潔に記述する。
  • データの検証や条件付き初期化を行う。

本記事では、2種類のうち「プライマリコンストラクタ」に焦点を当て、その定義方法や使用例を詳しく解説します。

コンストラクタの役割

コンストラクタ は、クラスのインスタンスを作成するときに自動的に呼び出され、インスタンスの初期化 を行います。

例えば「人」を表す Person クラスがあり、その人の名前と年齢を保持する変数があるとします。

このとき、以下のように初期化が行えます。

// Personクラスのインスタンスを生成し、定数personに代入
val person = Person("Taro", 25)	// 生成時に、プロパティに"Tarp"と25を引き渡す

この Person("Taro", 25) のカッコの中身がコンストラクタの役割です。これによりperson オブジェクトに「名前」と「年齢」を設定(初期化)できます。

※ 前回の「Lesson5-1:クラスの基本」の記事ではカッコの中に何も書いていませんでしたね。

Kotlinではこのような初期化を簡潔に行うために「プライマリコンストラクタ」を提供しています。

ポイント

「初期化」とは、オブジェクト(クラスのインスタンス)を作成する際に、そのオブジェクトが動作するために必要なデータや状態を設定することを指します。

Kotlinではコンストラクタや init ブロックを使って、こうした設定(プロパティの値を指定するなど)を行います。

プライマリコンストラクタとは?|initブロックによる初期化処理

プライマリコンストラクタ は、クラスの宣言時に定義される特別なコンストラクタです。

以下はその特徴です:

  1. クラス名の後ろに直接定義できる。
  2. 必要な引数を指定して、クラスを初期化できる。
  3. 初期化処理を initブロック で補足できる。

プライマリコンストラクタは次のように定義します。

class クラス名(引数: 型) {   // 引数を持つクラスの定義
    init{					// initブロックの定義
        // initブロックの処理内容(プライマリコンストラクタの初期化内容)
    }
    // クラス本体(プロパティやメソッドなど)
}
val 定数 = クラス名(引数)

例えば、以下のコードを見てみましょう。

class Person(val name: String, var age: Int) {	// 2つの引数を持つPersonクラスの定義
    init {										// initブロックの定義
        println("Personが生成されました: name=$name, age=$age")
    }
}
fun main() {							// メイン関数の宣言
    val person1 = Person("Taro", 25)	// 引数を渡してインスタンス生成
    val person2 = Person("Eve", 22) 	// 引数を渡してインスタンス生成
}

このコードではインスタンス生成時に引数を渡すことで、特にメソッドの呼び出し等をしなくてもクラス内のプライマリコンストラクタが実行されます。

出力は以下のようになります。

Personが生成されました: name=Bob, age=30
Personが生成されました: name=Eve, age=22

言い換えると、Personクラスが持つ二つのプロパティ(nameとage)の値を、インスタンス生成時に初期化(設定)し、それを用いてメソッドを実行したと言えます。

これがプライマリコンストラクタの役割です。

プライマリコンストラクタの使用例

プライマリコンストラクタには、クラスのプロパティを初期化するだけでなく、複雑な初期化処理が必要になる場合があります。

例えば以下のコードを見てみましょう。

class Person(val name: String, var age: Int) { // 2つのプロパティを持つPersonクラスの定義
    init { // initブロックの定義
        if (age < 0){
            println("年齢は0以上である必要があります")
        }else{
        	println("Person named $name with age $age is created.")
        }
    }
}

この設計図を基にインスタンス生成を行うと自動的にinitブロック内の処理が実行され、ageの値が0以下だった場合にメッセージを表示されます。

まとめ|プライマリコンストラクタを使いこなそう

プライマリコンストラクタは、Kotlinでクラスを定義するときにとても便利な仕組みです。

プロパティの宣言と初期化をシンプルにまとめることができ、可読性やメンテナンス性も向上します。

クラス設計の基本として、プライマリコンストラクタの使い方をしっかり押さえておくことで、よりスマートなKotlinプログラミングができるようになります。

ぜひ実際に自分でも使ってみて、その便利さを体感してみてください!

練習問題|プライマリコンストラクタで初期化とエラーチェックを実装しよう

ラスを作成する際に使用するプライマリコンストラクタを学びましょう。

この練習問題では名前と年齢を管理する Person クラスを作成し、以下の操作を行います:

  1. 名前と年齢を初期化する。
  2. 初期化後に自己紹介を表示する。
  3. 年齢を増やす処理を追加し、その結果を表示する。
    また、初期化時に年齢が不正な場合はエラーを表示するようにしましょう。

この問題の要件

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

  1. Person クラスを作成すること。
    • プライマリコンストラクタを使用して name(名前)と age(年齢)を初期化すること。
    • クラスの中に init ブロックを作成し、年齢が0未満の場合はエラーをスローすること。
    • 以下のように、組み込み関数requireを使用すること。
      require(age >= 0) { "年齢は0以上である必要があります。" }
  2. introduce メソッドを作成すること。
    • 自己紹介のメッセージを出力すること。
      例: 「こんにちは、私の名前は太郎です。年齢は20歳です。」
  3. haveBirthday メソッドを作成すること。
    • 年齢を1増やし、誕生日を祝うメッセージを表示すること。
      例: 「太郎さんが誕生日を迎えました!年齢は21歳になりました。」
  4. メイン関数を作成し、以下の操作を行うこと。
    • ユーザーから名前を入力すること。
    • ユーザーから年齢を入力すること。
    • Person クラスのインスタンスを作成し、自己紹介メソッドを呼び出すこと。
    • haveBirthday メソッドを呼び出して年齢を1増やすこと。
    • 不正な年齢が入力された場合はエラーメッセージを表示すること。

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

新しい人物が作成されました: 名前=太郎, 年齢=20
こんにちは、私の名前は太郎です。年齢は20歳です。
太郎さんが誕生日を迎えました!年齢は21歳になりました。

この問題を解くヒント

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

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

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

1:Personクラスを定義
  □ プライマリコンストラクタを使用してnameとageを初期化
  □ □ initブロックでageが0未満の場合に例外をスロー
  □ □ □ ”年齢は0以上である必要があります。” というメッセージを表示
  □ □ □ 新しいPersonオブジェクトの情報を出力
  □ haveBirthdayメソッドの定義
  □ □ ageを1増やす
  □ □ nameと更新されたageを含むメッセージを出力
  □ introduceメソッドの定義
  □ □ nameとageを含む自己紹介メッセージを出力
2:main関数を定義
  □ ”名前を入力してください:” というメッセージを出力
  □ ユーザー入力を取得してnameに代入(デフォルトは”名無し”)
  □ ”年齢を入力してください:” というメッセージを出力
  □ ユーザー入力を取得してageInputに代入
  □ ageInputを整数に変換し、失敗時は-1を代入
  □ tryブロックを開始
  □ □ Personオブジェクトをnameとageで初期化
  □ □ introduceメソッドを呼び出して自己紹介
  □ □ haveBirthdayメソッドを呼び出して年齢を1増加
  □ catchブロックを開始
  □ □ 例外メッセージを出力

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

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

// 人物を表すクラス
/*【穴埋め問題1】
ここでプライマリコンストラクタを使用して、名前(name)と年齢(age)を受け取るクラスを定義してください。
名前は読み取り専用、年齢は変更可能なプロパティにしてください。
*/

class Person(val name: String, var age: Int) {
    // 初期化処理:年齢が0未満の場合はエラーを出します
    init {
        /*【穴埋め問題2】
        ここでrequire関数を使って、年齢が0以上であることをチェックするコードを書いてください。
        チェックが失敗した場合は適切なエラーメッセージを表示してください。
        */
        println("新しい人物が作成されました: 名前=$name, 年齢=$age")
    }

    // 年齢を1歳増やす関数
    fun haveBirthday() {
        /*【穴埋め問題3】
        ここで年齢を1増やすコードを書いてください。また、その結果を出力するメッセージを追加してください。
        */
    }

    // 自己紹介を表示する関数
    fun introduce() {
        /*【穴埋め問題4】
        名前と年齢を使用して自己紹介のメッセージを出力するコードを書いてください。
        */
    }
}

fun main() {
    // 入力を通して新しい人物を作成します
    println("名前を入力してください:")
    val name = readLine() ?: "名無し" // 名前が入力されなかった場合は「名無し」にします

    println("年齢を入力してください:")
    val ageInput = readLine()
    val age = ageInput?.toIntOrNull() ?: -1 // 年齢が無効な場合は-1にします

    // クラスのインスタンスを作成
    try {
        /*【穴埋め問題5】
        ここでPersonクラスのインスタンスを作成し、introduceメソッドとhaveBirthdayメソッドを呼び出してください。
        */
    } catch (e: IllegalArgumentException) {
        println("エラー: ${e.message}")
    }
}

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

この問題の解答と解説

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

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

Q
正解コード
// 人物を表すクラス
// プライマリコンストラクタを使用して名前と年齢を初期化します
class Person(val name: String, var age: Int) {
    // 初期化処理:年齢が0未満の場合はエラーを出します
    init {
        require(age >= 0) { "年齢は0以上である必要があります。" }
        println("新しい人物が作成されました: 名前=$name, 年齢=$age")
    }

    // 年齢を1歳増やす関数
    fun haveBirthday() {
        age += 1
        println("$name さんが誕生日を迎えました!年齢は$age 歳になりました。")
    }

    // 自己紹介を表示する関数
    fun introduce() {
        println("こんにちは、私の名前は$name です。年齢は$age 歳です。")
    }
}

fun main() {
    // 入力を通して新しい人物を作成します
    println("名前を入力してください:")
    val name = readLine() ?: "名無し" // 名前が入力されなかった場合は「名無し」にします

    println("年齢を入力してください:")
    val ageInput = readLine()
    val age = ageInput?.toIntOrNull() ?: -1 // 年齢が無効な場合は-1にします

    // クラスのインスタンスを作成
    try {
        val person = Person(name, age) // プライマリコンストラクタを使用して初期化
        person.introduce() // 自己紹介
        person.haveBirthday() // 年齢を増やす
    } catch (e: IllegalArgumentException) {
        println("エラー: ${e.message}")
    }
}
Q
正解コードの解説

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

クラスの定義とプライマリコンストラクタ

class Person(val name: String, var age: Int) {
    init {
        require(age >= 0) { "年齢は0以上である必要があります。" }
        println("新しい人物が作成されました: 名前=$name, 年齢=$age")
    }
}
  1. class Person(val name: String, var age: Int)
    この部分でKotlinのクラスを定義しています。ここに記載されている valvar はプライマリコンストラクタの引数をクラスのプロパティ(変数)として直接定義しています。
    • val は読み取り専用のプロパティ(変更不可)。
    • var は変更可能なプロパティ。
  2. init ブロック
    プライマリコンストラクタで受け取ったデータを初期化するために使います。ここでは require を使って年齢が0未満の場合にエラーを発生させています。
    さらに、インスタンスが作成されるたびに初期化メッセージを出力します。

自己紹介メソッド

fun introduce() {
    println("こんにちは、私の名前は$nameです。年齢は$age歳です。")
}

このメソッドは、名前と年齢を含んだ自己紹介文を出力します。

$name$age は文字列テンプレートを使ってプロパティの値を埋め込んでいます。

誕生日メソッド

fun haveBirthday() {
    age += 1
    println("$nameさんが誕生日を迎えました!年齢は$age歳になりました。")
}

このメソッドではage プロパティを1増やしてから、更新後の年齢を含む誕生日メッセージを出力します。

age += 1 は、age = age + 1 の短縮形です。

メイン関数

fun main() {
    println("名前を入力してください:")
    val name = readLine() ?: "名無し"

    println("年齢を入力してください:")
    val ageInput = readLine()
    val age = ageInput?.toIntOrNull() ?: -1

    try {
        val person = Person(name, age)
        person.introduce()
        person.haveBirthday()
    } catch (e: IllegalArgumentException) {
        println("エラー: ${e.message}")
    }
}
  1. ユーザー入力 (readLine)
    ユーザーに名前と年齢を入力してもらい、それを nameage に保存します。
    • 名前が空の場合は「名無し」と設定します(?: 演算子を使用)。
    • 年齢が数字でない場合や入力が空の場合はデフォルトで -1 を設定します。
  2. 例外処理 (try-catch)
    Person クラスを初期化するとき、年齢が0未満の場合にエラー(IllegalArgumentException)が発生します。このエラーをキャッチして適切なメッセージを表示します。
Q
サイト改善アンケート & ご指摘/ご質問

本サイトでは、みなさまの学習をよりサポートできるサービスを目指しております。
そのため、みなさまの「プログラミングを学習する理由」などを アンケート 形式でお伺いしています。

また記事の内容に関する ご指摘ご質問 もお待ちしています。

1.このサイトをどのように活用していますか?また、今後どのように活用したいですか?
5.気になっているサービス・商品があれば教えてください。
※ 特定の記事に関する内容の場合は、記事番号(レッスン〇-〇)をご記入願います。

<<前のページ

学習記事一覧

次のページ>>

記事URLをコピーしました