【Kotlin】レッスン5-02:プライマリコンストラクタを理解しよう

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

この記事で学べる知識:プライマリコンストラクタ

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

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

Kotlinの「プライマリコンストラクタ」とは

この章ではKotlinにおける「プライマリコンストラクタ」の意味や使い方を学習します。用語の解説が不要な方はここをクリックして練習問題へ飛びましょう。



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

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

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

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

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

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

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

コンストラクタとは?

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

具体例を考えましょう: 例えば「人」を表す Person クラスがあり、その人の名前と年齢を保持するとします。

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

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

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

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

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

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

例えば、以下のようなイメージです:

  • 現実の例: 新しいノートを買ったら、最初に名前を書く(=初期化)。
  • プログラムの例: Person クラスのインスタンスを作るときに、名前や年齢を設定する。

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

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

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

以下はその特徴です:

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

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

class クラス名 constructor(引数: 型, ...) {
    // クラス本体
}

しかしconstructor キーワードは省略することが一般的で、以下のように書くことができます。

class Person(val name: String, var age: Int)
  • class Person はクラス名を表します。
  • (val name: String, var age: Int) はプライマリコンストラクタの引数リストです。
  • valvar を使用しているため、この引数はクラスのプロパティ(変数)として扱われます。

initブロックの役割

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

このような処理は「init ブロック」で記述できます。

class Person(val name: String, var age: Int) { // 2つのプロパティを持つPersonクラスの定義
    init { // initブロックの定義
        require(age > 0) { "Age must be greater than 0" }
        println("Person named $name with age $age is created.")
    }
}
  • init ブロック内のコードは、クラスのインスタンス作成時に自動的に実行されます。
  • 上記では年齢が0以下の場合にエラーを発生させる処理を追加しています。

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

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

以下はプライマリコンストラクタを使った実際の例です。

// 3つのプロパティを持つCarクラスの定義
class Car(val brand: String, val model: String, val year: Int) {
    init {
        require(year > 1885) { "Year must be after 1885" }
        println("A $brand $model from $year has been created.")
    }
}

fun main() {
    val myCar = Car("Toyota", "Corolla", 2020) // インスタンス生成し、3つのデータを渡す
    println("Brand: ${myCar.brand}")
    println("Model: ${myCar.model}")
    println("Year: ${myCar.year}")
}

このコードの動作:

  1. Car クラスは、brandmodelyear の3つのプロパティを持っています。
  2. init ブロックは、年式が1885年よりも前であればエラーを発生させます。
  3. インスタンス作成時に、情報が初期化され、println 文が実行されます。

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

A Toyota Corolla from 2020 has been created.
Brand: Toyota
Model: Corolla
Year: 2020

まとめ

Kotlinのプライマリコンストラクタは、クラスの初期化を簡潔に記述するための強力なツールです。

特に「引数リスト」と「initブロック」を組み合わせることで、柔軟かつ効率的な初期化が可能となります。

クラスの基本的な構造を理解し、プライマリコンストラクタを適切に使えるようになると、Kotlinのプログラミングがさらに快適になります。

次回の記事ではセカンダリコンストラクタを取り上げます。これを理解することで、さらに高度な初期化方法を学べます!

プライマリコンストラクタの練習問題:初期化の仕組みを理解しよう

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

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

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

この問題の要件

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

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

ただし、以下のような実行結果となるコードを書くこと。

*****↓↓正解コードの実行結果の例↓↓*****

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

この問題を解くヒント

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

ヒント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ブロックを開始
  □ □ 例外メッセージを出力

ヒント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}")
    }
}

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

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



練習問題の解答と解説

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

正解コードの例

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

// 人物を表すクラス
// プライマリコンストラクタを使用して名前と年齢を初期化します
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}")
    }
}

正解コードの解説

今回のコードでは「プライマリコンストラクタ」を使ってクラスの初期化を行い、名前と年齢を管理するシンプルなプログラムを作成しました。

以下にコードの各部分を初心者向けに詳しく解説します。

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

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)が発生します。このエラーをキャッチして適切なメッセージを表示します。

まとめ

このコードを通じて、以下のポイントを学ぶことができます:

  1. プライマリコンストラクタを使ったクラスの初期化方法。
  2. init ブロックで複雑な初期化処理やデータの検証を行う方法。
  3. ユーザー入力を受け取り、例外処理を用いてエラーを処理する方法。

特にプライマリコンストラクタは、Kotlinのクラス定義をシンプルかつ効率的に記述するために非常に便利な機能です。

この機会に、プライマリコンストラクタと init ブロックの使い方をしっかり理解してください!

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

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

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

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






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