kotlinのnullについて

## 経緯
kotlinはjavaに比べて、随分とエンジニアに親切になったように思います。
個人的に嬉しいのは所謂null安全です。
これがあることで取り扱いがかなり楽になり、NullPointerExceptionなんて久しく見ておりません。
しかし最近になって知った ~~仕様~~ 挙動（※） があったので、自戒を込めて基礎的な部分をまとめてみることにしました。
※2021/11/5 言語仕様ではなく、標準ライブラリでの定義による動作です。誤解を招く表現で申し訳ありませんでした。
参考にさせていただいた記事：[【Kotlin】toString() 関数](https://qiita.com/sdkei/items/3f5c06738804a5af896a)

## なにがあったか
``` Sample.kt
val hoge: Int? = null

if (hoge != null) {
    Log.d("test", "hoge is not null.")
} else {
    Log.d("test", "hoge is null.")
}

val fuga = hoge.toString()

if (fuga != null) {
    Log.d("test", "fuga is not null.")

    if (fuga == "null") {
        Log.d("test", "fugaValue is \"null\".")
    } else {
        Log.d("test", "fugaValue is $fuga.")
    }
} else {
    Log.d("test", "fuga is null.")
}

// 以下は確認のため
val piyo: String? = null

if (piyo != null) {
    Log.d("test", "piyo is not null.")

    if (piyo == "null") {
        Log.d("test", "piyoValue is \"null\".")
    } else {
        Log.d("test", "piyoValue is $piyo.")
    }
} else {
    Log.d("test", "piyo is null.")
}

```

上記のようなものがあったとして、
nullの可能性があるものに対してtoString()の時点でそもそもエラーになると思ったのですが
そんなことはなく・・・。実行すると以下が出力されます。

```
2021-11-05 09:38:23.816 3289-3289/com.example.testapplication D/test: hoge is null.
2021-11-05 09:38:23.816 3289-3289/com.example.testapplication D/test: fuga is not null.
2021-11-05 09:38:23.816 3289-3289/com.example.testapplication D/test: fugaValue is "null".
2021-11-05 09:38:23.816 3289-3289/com.example.testapplication D/test: piyo is null.
```

hogeはnull、fuga（hoge.toString()したもの）はnullではない、fugaの中身（fugaValue）は文字列"null"という結果になりました。piyoはちゃんとnullです。
したがって、kotlinのtoString()は安全、ということです。
（文字列の"null"が返ってくるのは少し嫌な気持ちになりますね・・・。チェックする際は注意しなくてはいけません。）
ただし、toInt()などはだめです。nullは数値にできませんしね。

## サンプル
kotlinのnull周りの仕様について少しだけまとめます。

### 型宣言
kotlinはnull許容かどうかを明示的に記述する必要があります。

``` Sample.kt
// これはだめ
val hoge : String = null
// これはOK
val fuga : String? = null
```
「?」がついていない変数はnullでないと言えます。
ただし、プログラム上の不備でnullが入ってくると以下のようなエラーになります。


``` NullTestSampleKotlin.kt
class NullTestSampleKotlin {
    fun test(str: String) {
        Log.d("test", str)
    }
}
```

``` NullTestSample.java
public class NullTestSample {
    public void test() {
        NullTestSampleKotlin nullTestSampleKotlin = new NullTestSampleKotlin();
        nullTestSampleKotlin.test(null);
    }
}
```


```
Caused by: java.lang.NullPointerException: Parameter specified as non-null is null
```
kotlinでメソッドを定義、引数のStringなどに「?」をつけなかった場合、
kotlinからは引数がnon-nullなことはわかりますが、Javaからはわからずにnullを渡せてしまいます。
Javaからkotlinに移行する際などは注意しましょう。

### スマートキャスト
nullでないことが確認できていれば、安全に使うことができます。
そこをよしなにやってくれるのがスマートキャストです。

以下の例で、nullチェックをしたブロックの中であればhogeがIntであることは確定しているので、toLong()などを呼び出せます。
ブロック外の呼び出しはコンパイルエラーになります。nullかもしれないですからね。

``` Sample.kt
val hoge : Int? = null

if (hoge != null) {
    // hogeをIntとして扱える
    val hogeLong = hoge.toLong()
}

// これはだめ。
val hogeLong2 = hoge.toLong()
```

### セーフコール
以下のように「?」に続けて呼び出すことで、
nullでなかった場合のみ呼び出しが成功します。
失敗の場合はnullが返却されます。

``` Sample.kt
val hoge : Int? = null

// nullが返ってくる可能性があるので、hogeLongの型は「Long?」になります。
val hogeLong = hoge?.toLong()
```

めちゃくちゃ使います。

### エルヴィス演算子
「?:」を用いることにより、左辺がnullだった場合に右辺を使用してくれます。
エルヴィス演算子が連続することもありますが、少しわかりづらくなりますね。

``` Sample.kt
// hogeがnullでない場合toLong()の結果、nullだった場合は-1LがhogeLongに代入されます。
// したがってhogeLongの型は「Long」に確定しています。
val hogeLong = hoge?.toLong() ?: -1L
```

あのロックな方に見えることからその名前がついているそうです。

## 終わりに
Javaよりもチェックが厳格になったことにより、記述するのは少しだけ面倒になりましたが
品質は良くなりやすいのかなあと思います。
たまにJavaのコードを書く際に混乱してしまうので、注意してコードを書いていきたいです。