Kotlinで変数をNonNullにする4つの方法

Kotlinの特徴の一つにNullSafetyの仕組みがあります。
Javaではif文で判定するしかなかったNull判定を、変数宣言時や代入時に行うことで後続処理での`NullPointerException`の発生を防げます。

そこで、KotlinでのNonNullで変数を定義する方法をまとめます。

# 対象読者

Kotlinをがっつりとは書いていない方
最近AndroidアプリをKotlinに移行し始めた方

# lateinitを使う
lateinitは変数宣言に初期値を代入せず、後から代入されることを約束する際につけます。

``` lateinit.kt
class LateInit{
    var user:User // コンパイルエラー
    var user2:User? = null // エラーにはならないけどNullable😵
    lateinit var user3:User //Good👍
    lateinit val user4:User // valはfinal扱いなので、あとから代入はできないためコンパイルエラー
}
```

## 使う所
Androidを開発しているとよく使います
ActivityやFragmentは、システム側がインスタンスを生成するため、引数付きコンストラクタやinitを使う事はできません。
そのため、各値の初期化はonCreate()内にて行います。

``` MainActivity.kt
class MainActivity(){
    lateinit var user: User

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val name = getIntent().getStringExtra("name") ?: ""
        user = User(name)
    }
   //...
}

```

## 代入する前に呼び出したら…
落ちます

``` UnInit.kt
class UnInit {
    lateinit var user: User

    fun run() {
        println(user) //ここで落ちます
        println(user.name) //ここには到達しない
    }
}
```

出力結果

``` 出力結果
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property user has not been initialized
	at NonNull.run(NonNull.kt:5)
	at MainKt.main(Main.kt:6)
```

lateinitを宣言した変数に初期化されていない時専用の例外が出ます。
Javaで同じ様に書くと、一行下に書いた``` user.name ``` でNullPointerExceptionが出力されるはずなのでより正確に例外の原因がわかります


# by lazyを使う
変数を最初に呼び出されるまで初期化を遅延します。

``` lazy.kt
    val user: User by lazy {
        User("John")
    }
    fun onCreate3() {
        print("${user}")
    }
```

実行結果

```
User(name=John)
```

## 注意点
by lazyによって取得された値は、AndroidのLifecycle関係なく保持されます
findViewByIdで取得したレイアウトはFragmentが再生成された場合でも古いViewを取得し続けます
参考URL:https://qiita.com/takahirom/items/49e18ec7084bb3b1937e
kotlinを使い始めた時、
変数をvalに統一したい時によく使っていたのですが良くない実装だったので今はlateinitや[Kotlin Android Extensions
](https://kotlinlang.org/docs/tutorials/android-plugin.html)を使っています。

## 使うところ
上記理由により、今はあまり使う場面に遭遇していないです。
取得処理が重いファイル読み込みなどに使う事はあるかもしれません

# ?: を使う(エルビス演算子)
Nullableの型をそのまま使うと不正な文字列になったりクラッシュになったりする

``` ShowUser.kt
    fun showUser(user: User?) {
        val showName = user?.name // nullableなので、?.nameにしないとコンパイルエラー
        println("名前:$showName") // showNameはString?になる😵
    }

    fun run(){
        showUser(User("John"))
        showUser(null) // アプリの画面にnullって出る😭
    }
```

出力結果

``` 
名前:John
名前:null
```

なので、Java同様Nullチェックが必要になる

``` showUser.kt
    fun showUser(user: User?) {
        if(user == null)return
        val showName = user.name // nullじゃないのは確定したため、?.じゃなくてもコンパイルが通る
        println(showName) // showNameはStringになる
    }

```

Kotlinではif文の他に``` ?: ```というエルビス演算子を使って書くことができる

``` showUser.kt
    fun showUser(user: User?) {
        val showName = user?.name ?: return // userがnullなら代入せず離脱する
        val showName2 = user?.name ?: "名無し" // userがnullならshowName2に"名無し"を代入する
        val showName3 = user?.name ?: throw Exception() // userがnullなら例外を投げる
        println(showName)
    }

```

# letを使う(スコープ関数)

## ?:だとうまくいかない場合
エルビス演算子は便利だけど、メンバ変数をNullチェックをしたい時にはすんなり行かない

``` showUser.kt
class ShowUser(){
    var user: User? = null

    fun showUser() {
        if (user == null) return
        val showName = user.name
        val showAge = user.age
        println("名前:$showName 年齢:$showAge")
    }
}
```
こういうコードを書くと、``` Smart cast to 'User' is impossible, because 'user' is a mutable property that could have been changed by this time ``` とでてコンパイルエラーになる
``` user ``` メンバ変数として定義しているため、Nullチェック後に値が変わるかもしれない(例えば、非同期処理で同時に```user```に値を代入する)
そのためuserをNullチェック後でもNonNullとして扱うことができず、Nullableのままになってしまう

## 解決方法
``` showUser.kt
    var user: User? = null

    fun showUser() {
        user?.let { user ->
            val showName = user.name
            val showAge = user.age
            println("名前:$showName 年齢:$showAge")
        }
    }
```
?.とスコープ関数を使って、関数内はNonNullであることを確定させた状態で処理を行う

``` showUser.kt
        user?.let { user ->
            //...
        } ?: throw Exception() //エルビス演算子と組み合わせる事もできる(nullの時例外を投げる)
```



# あとがき
ここで紹介した方法以外にNonNullにする方法はあると思います。
知っている方はコメントで紹介していただけると嬉しいです😊