value classの挙動

value classの内部でcompanion objectを使う場合に、classやdata classと挙動が違ったので注意が必要

# まとめ
- value classでは　class→companion objectの順でinitが実行される
- class ではcompanion object→classの順でinitが実行される
- そのためvalue classのcompanion objectで定義した値をclassのinit内で使うとNullPointerExceptionが発火する

# 挙動

まずは違いを知るためにclassの場合の挙動
[コード](https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS42LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiY2xhc3MgVCh2YWwgdmFsdWU6IEZsb2F0KSB7XG4gICAgY29tcGFuaW9uIG9iamVjdCB7XG4gICAgICAgIGluaXQge1xuICAgICAgICAgICAgcHJpbnRsbihcImNvbXBhbmlvbiBvYmplY3QgaW5pdFwiKVxuICAgICAgICB9XG4gICAgICAgIHZhbCBOID0gVCgwLjBmKVxuICAgICAgICB2YWwgcmFuZ2UgPSAwZi4uMWZcbiAgICB9XG4gICAgXG4gICAgaW5pdCB7XG4gICAgICAgIHByaW50bG4oXCJjbGFzcyBpbml0XCIpXG4gICAgfVxufVxuXG5mdW4gbWFpbigpIHtcbiAgICBwcmludGxuKFwibWFpbiByYW5nZT0ke1QucmFuZ2V9XCIpXG59In0=)

``` kotlin
class T(val value: Float) {
    companion object {
        init {
            println("companion object init")
        }
        val N = T(0.0f)
        val range = 0f..1f
    }
    
    init {
        println("class init")
    }
}

fun main() {
    println("main range=${T.range}")
}
```

実行結果

```
companion object init
class init
main range=0.0..1.0
```

companion objectのinitが実行されてからclassのinitが実行される。


次にvalue classの場合
[コード](https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS42LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiQEp2bUlubGluZVxudmFsdWUgY2xhc3MgVCh2YWwgdmFsdWU6IEZsb2F0KSB7XG4gICAgY29tcGFuaW9uIG9iamVjdCB7XG4gICAgICAgIGluaXQge1xuICAgICAgICAgICAgcHJpbnRsbihcImNvbXBhbmlvbiBvYmplY3QgaW5pdFwiKVxuICAgICAgICB9XG4gICAgICAgIHZhbCBOID0gVCgwLjBmKVxuICAgICAgICB2YWwgcmFuZ2UgPSAwZi4uMWZcbiAgICB9XG4gICAgXG4gICAgaW5pdCB7XG4gICAgICAgIHByaW50bG4oXCJjbGFzcyBpbml0XCIpXG4gICAgfVxufVxuXG5mdW4gbWFpbigpIHtcbiAgICBwcmludGxuKFwibWFpbiByYW5nZT0ke1QucmFuZ2V9XCIpXG59In0=)

``` kotlin
@JvmInline
value class T(val value: Float) {
    companion object {
        init {
            println("companion object init")
        }
        val N = T(0.0f) // 定義を追加
        val range = 0f..1f
    }
    
    init {
        println("class init")
    }
}

fun main() {
    println("main range=${T.range}")
}
```

実行結果

```
class init
companion object init
main range=0.0..1.0
```

この場合、なんとclass実装と違ってvalue classのinitが実行されてからcompanion objectのinitが実行される。

そのため、以下のようにcompanion object内で定数を定義し、value classのinitで値のチェックを行うようにするとエラーとなる
[コード](https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS42LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiQEp2bUlubGluZVxudmFsdWUgY2xhc3MgVCh2YWwgdmFsdWU6IEZsb2F0KSB7XG4gICAgY29tcGFuaW9uIG9iamVjdCB7XG4gICAgICAgIHZhbCBOID0gVCgwLjBmKVxuICAgICAgICBwcml2YXRlIHZhbCByYW5nZSA9IDBmLi4xZlxuICAgIH1cbiAgICBcbiAgICBpbml0IHtcbiAgICAgICAgY2hlY2sodmFsdWUgaW4gcmFuZ2UpXG4gICAgfVxufVxuXG5mdW4gbWFpbigpIHtcbiAgICBwcmludGxuKFQuTilcbn0ifQ==)

``` kotlin
@JvmInline
value class T(val value: Float) {
    companion object {
        val N = T(0.0f)
        private val range = 0f..1f
    }
    
    init {
        check(value in range)
    }
}

fun main() {
    println(T.N)
}
```

出るException

```
Exception in thread "main" java.lang.ExceptionInInitializerError
 at FileKt.main (File.kt:14) 
 at FileKt.main (File.kt:-1) 
 at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (:-2) 
Caused by: java.lang.NullPointerException
at T .constructor-impl(File.kt:9)
at T .&amp;lt;clinit&amp;gt;(File.kt:4)
at FileKt .main(File.kt:14)
```


他にも面白い動きがあり、value classのcompanion object内の定数にアクセスするだけだとinitは呼ばれない
[コード](https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS42LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiQEp2bUlubGluZVxudmFsdWUgY2xhc3MgVCh2YWwgdmFsdWU6IEZsb2F0KSB7XG4gICAgY29tcGFuaW9uIG9iamVjdCB7XG4gICAgICAgIGluaXQge1xuICAgICAgICAgICAgcHJpbnRsbihcImNvbXBhbmlvbiBvYmplY3QgaW5pdFwiKVxuICAgICAgICB9XG4gICAgICAgIHZhbCByYW5nZSA9IDBmLi4xZlxuICAgIH1cbiAgICBcbiAgICBpbml0IHtcbiAgICAgICAgcHJpbnRsbihcImNsYXNzIGluaXRcIilcbiAgICB9XG59XG5cbmZ1biBtYWluKCkge1xuICAgIHByaW50bG4oXCJtYWluIHJhbmdlPSR7VC5yYW5nZX1cIilcbn0ifQ==)

``` kotlin
@JvmInline
value class T(val value: Float) {
    companion object {
        init {
            println("companion object init")
        }
        val range = 0f..1f
    }
    
    init {
        println("class init")
    }
}

fun main() {
    println("main range=${T.range}")
}
```

結果

```
main range=0.0..1.0
```

この場合、classのinitすら呼ばれない

同じ用にdata classで実行するとrangにアクセスするだけでcompanion objectのinitが実行される 
[コード](https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS42LjEwIiwicGxhdGZvcm0iOiJqYXZhIiwiYXJncyI6IiIsIm5vbmVNYXJrZXJzIjp0cnVlLCJ0aGVtZSI6ImlkZWEiLCJjb2RlIjoiZGF0YSBjbGFzcyBUKHZhbCB2YWx1ZTogRmxvYXQpIHtcbiAgICBjb21wYW5pb24gb2JqZWN0IHtcbiAgICAgICAgaW5pdCB7XG4gICAgICAgICAgICBwcmludGxuKFwiY29tcGFuaW9uIG9iamVjdCBpbml0XCIpXG4gICAgICAgIH1cbiAgICAgICAgdmFsIHJhbmdlID0gMGYuLjFmXG4gICAgfVxuICAgIFxuICAgIGluaXQge1xuICAgICAgICBwcmludGxuKFwiY2xhc3MgaW5pdFwiKVxuICAgIH1cbn1cblxuZnVuIG1haW4oKSB7XG4gICAgcHJpbnRsbihcIm1haW4gcmFuZ2U9JHtULnJhbmdlfVwiKVxufSJ9)

``` kotlin
data class T(val value: Float) {
    companion object {
        init {
            println("companion object init")
        }
        val range = 0f..1f
    }
    
    init {
        println("class init")
    }
}

fun main() {
    println("main range=${T.range}")
}
```

```
companion object init
main range=0.0..1.0
```