🐦

Kotlin コンストラクタの使い分け

に公開

はじめに

KotlinのClassのコンストラクタは、

  • プライマリコンストラクタ
  • セカンダリコンストラクタ
    の2パターンが存在する。

それぞれの違いを説明する。

結論

  • 基本的にクラスプロパティの初期化を行うだけならプライマリコンストラクタだけでOK
  • 複雑な処理(バリデーションをしたい、ちょっと計算して初期値を入れたいなど)の場合はプライマリコンストラクタでプロパティを渡しつつ、initブロックで処理を行う
  • 複数の複雑な処理、異なる初期化処理のパターンがあるなど限定的なパターンの場合のみ、セカンダリコンストラクタを使用する

詳細な説明

プライマリコンストラクタ

  • その名の通り、最も上位にあるコンストラクタ。
  • クラスが初期化された時に最も最初に呼び出される。
  • プライマリコンストラクタで初期化したプロパティはinitで利用することができる。
// プライマリコンストラクタの基本形
class User(val name: String, val email: String) {
    // nameとemailはクラスのプロパティとして自動的に定義される
}

// 使用例
val user = User("山田太郎", "yamada@example.com")
println(user.name)  // 山田太郎

プライマリコンストラクタ+initブロック

  • プロパティの初期化と追加処理を行いたい場合にinit内に処理を記載する
  • バリデーションや計算などを行うことが多い
class BankAccount(val accountNumber: String, initialBalance: Double) {
    var balance: Double
    
    init {
        // バリデーション
        require(initialBalance >= 0) { "初期残高は0以上である必要があります" }
        require(accountNumber.matches(Regex("\\d{10}"))) { "口座番号は10桁の数字である必要があります" }
        
        // 初期化処理
        balance = initialBalance
        println("口座番号 $accountNumber が作成されました。残高: $balance 円")
    }
}

// 使用例
val account = BankAccount("1234567890", 10000.0)
// 出力: 口座番号 1234567890 が作成されました。残高: 10000.0 円

セカンダリコンストラクタ

  • プライマリコンストラクタとinitの処理が完了した後に呼び出される。
  • セカンダリコンストラクタは複数作成することができる。
class Employee(val name: String) {
    var age: Int = 0
    var department: String = "未配属"
    
    // セカンダリコンストラクタ1: 年齢も指定
    constructor(name: String, age: Int) : this(name) {
        this.age = age
        println("年齢情報を追加: $age 歳")
    }
    
    // セカンダリコンストラクタ2: 年齢と部署を指定
    constructor(name: String, age: Int, department: String) : this(name, age) {
        this.department = department
        println("部署情報を追加: $department")
    }
}

// 使用例
val emp1 = Employee("田中花子")  // プライマリコンストラクタのみ
val emp2 = Employee("佐藤次郎", 25)  // セカンダリコンストラクタ1
val emp3 = Employee("鈴木三郎", 30, "営業部")  // セカンダリコンストラクタ2

具体的な実行順序は?

実行される順序は以下の通り

  1. プライマリコンストラクタのパラメータ処理
  2. プロパティ初期化とinitブロックを宣言順に実行
  3. セカンダリコンストラクタの本体を実行
  4. セカンダリコンストラクタが複数ある場合には順に実行

実際のコードに落とし込んでみると、以下のような形になる。

class Person(val name: String) {
    
    // ① プライマリコンストラクタのパラメータ処理後、ここから順番に実行
    
    init {
        println("1. initブロック実行")
    }
    
    // ② セカンダリコンストラクタ1
    constructor(name: String, age: Int) : this(name) {
        println("2. セカンダリコンストラクタ1実行")
    }
    
    // ③ セカンダリコンストラクタ2
    constructor(name: String, age: Int, city: String) : this(name, age) {
        println("3. セカンダリコンストラクタ2実行")
    }
}

Discussion