Kotlinを使いこなす 〜Convert Java File to Kotlin File 卒業への道〜 その1

# はじめに
Android等でKotlinを使用している場合、Javaから『Convert Java File to Kotlin File』で自動変換している人は多い。実際にそれでも動くのだが、そこをもう1歩先に進みKotlinをより使いこなす為のメモ。

今回はスマートキャストを習得する。
Kotlinバージョン：1.3

# Kotlin変換前のサンプルコード
もっと良いサンプルあっただろうと思われるかもしれないが(汗)、以下のような Java と Kotlin が混ざったコードがあるとする。

```java:ResponseModle.java
public class ResponseModle {

    public void receiveResponseBody(ItemData data) {
        if (data == null) {
            return;
        }

        if (data.getBody() != null) {
            ResponseLog.INSTANCE.showLength(data.getBody());
        }

        // android.text.TextUtils.isEmpty(CharSequence str)
        // null or empty の両方をチェックできる
        if (!TextUtils.isEmpty(data.subBody)) {
            ResponseLog.INSTANCE.showSubTitle(data.getSubBody());
        }
    }
```
```java:ItemData.java
public class ItemData {

    private String body = null;

    private String subBody = null;

    public ItemData(String body, String subBody) {
        this.body = body;
        this.subBody = subBody;
    }

    public String getBody() {
        return body;
    }

    public String getSubBody() {
        return subBody;
    }
}
```
```kotlin:ResponseLog.kt
object ResponseLog {

    public fun showLength(title: String) {
        System.out.print(title.length)
    }

    public fun showSubTitle(subTitle: String) {
        System.out.print(subTitle)
    }
}
```

#Kotlin変換後のサンプルコード
これを自動変換して元の形に合わせるときっとこんな感じに・・・。

```kotlin:ResponseModle.kt
class ResponseModle {

    fun receiveResponceBody(data: ItemData?) {
        if (data == null) {
            return
        }

        if (data?.body != null) {
            ResponseLog.showLength(data.body)
        }

        if (!TextUtils.isEmpty(data?.subBody)) {
            ResponseLog.showSubTitle(data.subBody!!)
        }
    }
}
```
```kotlin:ItemData.kt
data class ItemData(val body: String? = null, val subBody: String? = null)
```
```kotlin:ResponseLog.kt
object ResponseLog {

    public fun showLength(title: String) {
        System.out.print(title.length)
    }

    public fun showSubTitle(subTitle: String) {
        System.out.print(subTitle)
    }
}
```

#何が問題か
ResponseModleクラスの実装がやや強引な実装になってしまった。
ItemData のnullチェック完了しているのに判定でNullableのアンラップ(data?・・・)してしまっている。
ResponseLogクラスのメソッドの引数がNonNullしか渡せない。コンパイルエラーを回避する為に強制アンラップ(・・・!!)している。既にnullチェック済みであるので強制アンラップしたくない。

#スマートキャストで解決
スマートキャストを使用すると強制アンラップしなくてもコンパイルが通る。

```kotlin:ResponseModle.kt
class ResponseModle {

    fun receiveResponceBody(data: ItemData?) {
        // スマートキャスト
        if (data !is ItemData) return
        // スマートキャスト
        if (data.body is String) ResponseLog.showLength(data.body)
        // Kotlin1.3からisNullOrEmptyでスマートキャストが効くようになった
        if (!data.subBody.isNullOrEmpty()) ResponseLog.showSubTitle(data.subBody)
    }
}
```

#良くない例
強制アンラップを回避するためのエルビス演算子を使用してしまう
ResponseLog.showSubTitle(data.subBody ?: "")

#おまけ
値代入時のletを使用したシンプルな実装方法

こんな実装があったとして

```java:ResponseModle.java
public class ResponseModle {

    public void receiveResponseBody(ItemData data) {
        String body;
        if (data.getBody() == null) {
            return;
        } else {
            body = "OK";
        }
        ResponseLog.INSTANCE.showLength(body);
    }
}
```
Convert Java File to Kotlin File で変換すると

```kotlin:ResponseModle.kt
class ResponseModle {

    fun receiveResponceBody(data: ItemData?) {
        val body: String
        if (data?.body == null) {
            return
        } else {
            body = "OK"
        }
        ResponseLog.showLength(body);
}
```
となるが、Javaっぽい実装になってしまう。せっかくKotlinを使用するのであればletを使用してシンプルに書く

```kotlin:ResponseModle.kt
class ResponseModle {

    fun receiveResponceBody(data: ItemData?) {
        val body = data?.body?.let { "OK" } ?: return
        ResponseLog.showLength(body);
}
```
とてもシンプル！