Kotlin - クラス - コンストラクタ

公開日:2019-11-21 更新日:2019-11-25
[Kotlin]

1. 概要

コンストラクタは、クラスのインスタンス生成時に、初期化処理を行うことができます。


2. コンストラクタ


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

プライマリーコンストラクタは、他の言語のようなメソッドはありません。
インスタンスの生成に必要な値を受け取るだけです。
値を受け取った直後に、init ブロックが実行されるため、そこで初期化処理を行えます。
プライマリーコンストラクタは、クラス名の後ろに、関数の引数を指定するような形で定義します。
プライマリーコンストラクタを省略しても、init ブロックやプロパティの初期化は実行されます。

プライマリーコンストラクタの引数でプロパティを初期化
// class Test constructor (value1:Int, value2:Int) {  // constructor を省略しない書き方

class Test(value1:Int, value2:Int) {
    private val v1:Int = value1 * 10  // コンストラクタの引数で初期化
    private val v2:Int = value2 * 10  // コンストラクタの引数で初期化 

    fun test():Int {
        return v1 + v2
    }
}

fun main() {
    val obj = Test(100, 200)
    println(obj.test())  // 3000
}

init ブロックでプロパティの初期化
class Test(value1:Int, value2:Int) {
    private val v1:Int
    private val v2:Int
    init {
        v1 = value1 * 10  // コンストラクタの引数で初期化
        v2 = value2 * 10  // コンストラクタの引数で初期化
    }

    fun test():Int {
        return v1 + v2
    }
}

fun main() {
    val obj = Test(100, 200)
    println(obj.test())  // 3000
}

また、プライマリーコンストラクタの引数に var または val を付けると、自動的にプロパティが作成されます。
class Test(val value1:Int, var value2:Int)

fun main() {
    val obj = Test(100, 200)
    println(obj.value1)  // 100
    println(obj.value2)  // 200
}


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

クラス内の constructor() がセカンダリーコンストラクタです。
プライマリーコンストラクタがある場合は、セカンダリーコンストラクタの後ろに「:」をつけて、必ずプライマリーコンストラクタを指定する必要があります。

処理の流れは以下のようなイメージになります。
セカンダリーコンストラクタ {
  プライマリーコンストラクタ
  init { }
  
  セカンダリーコンストラクタの内部の処理
}

class Test(value1:Int) {

    init {
        println("init-1")
    }

    //セカンダリーコンストラクタ
    constructor(value1:Int, value2:Int) : this(value1) {
        println("constructor")
    }

    init {
        println("init-2")
    }
}

fun main() {
    val obj = Test(100, 200)
}

実行結果
init-1
init-2
constructor

プライマリーコンストラクタがない場合は、
セカンダリーコンストラクタを他の言語のコンストラクタのように扱うことができます。
また、init ブロックは、セカンダリーコンストラクタの実行前に実行されます。
class Test {

    init {
        println("init-1")
    }

    //セカンダリーコンストラクタ
    constructor() {
        println("constructor-1")
    }
    constructor(value1:Int) {
        println("constructor-2")
    }
    constructor(value1:Int, value2:Int) {
        println("constructor-3")
    }

    init {
        println("init-2")
    }
}

fun main() {
    val obj1 = Test()
    val obj2 = Test(1)
    val obj3 = Test(1,2)
}

実行結果
init-1
init-2
constructor-1
init-1
init-2
constructor-2
init-1
init-2
constructor-3

2.3 コンストラクタの隠蔽

クラス名の後ろに private constructor() と付けると、コンストラクタを隠蔽できます。

class Test private constructor() {
    val value = 100

    // companion object は、static メソッドにするためのおまじない
    companion object {

        fun getInstance(): Test {
            return Test()
        }

    }
}

fun main() {
    //val obj = Test() // エラー
    val obj = Test.getInstance()
    println(obj.value)  // 100
}


3. 注意事項

プライマリーコンストラクタがない時にセカンダリーコンストラクタを定義すると、
引数なしでインスタンスを生成することができなくなります。
class Test {
    constructor(v1:Int, v2:Int) { }
}
fun main() {
    // val obj1 = Test()   // コンパイルエラー

    val obj2 = Test(1, 2)  // これは正常
}

引数なしのセカンダリーコンストラクタを定義すれば、上記のエラーを回避できます。
class Test {
    constructor() { }
    constructor(v1:Int, v2:Int) { }
}
fun main() {
    val obj1 = Test()
    val obj2 = Test(1, 2)
}

プライマリーコンストラクタがある場合は、セカンダリーを使わずに、プライマリーだけを使ってインスタンスの生成を行うこともできます。
class Test(v1:Int) {
    constructor(v1:Int, v2:Int) : this(v1) { }
}
fun main() {
    val obj1 = Test(1)    // セカンダリーを使わずに、プライマリーだけを使う。
    val obj2 = Test(1, 2) // セカンダリーとプライマリーを使う。
}