アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Kotlinのスコープ関数との付き合い方

Androidアプリ開発担当のhondaです。 皆さんKotlinのスコープ関数使ってますか? 好きなスコープ関数はalsoです。

結論

Android版いこーよではスコープ関数はlet、alsoそしてrunを使うようにしています。 今回はなぜそのようにしているのか解説したいと思います。

スコープ関数のおさらい

Kotlinのスコープ関数とはKotlinの標準ライブラリに含まれている関数です。 オブジェクトに対してその関数を呼び出すと一時的に形成されたスコープの中で名前をつけることなくオブジェクトに参照できます。 それによりコードをより簡潔で読みやすいものにすることができます。 現在定義されているスコープ関数はlet, run, with, apply、そしてalsoです。*1

Person("Alice", 20, "Amsterdam").let {
    // 中括弧の中でPersonオブジェクトに"it"で参照することができます。
    println(it)
    it.moveTo("London")
    it.incrementAge()
    println(it)
}

withとapplyを使わない理由

それは、オブジェクトへの参照に"this"を使うからです。 これはwithとapplyではオブジェクトをラムダレシーバーとして参照しているからですがこの場合、クラスへの参照の"this"と混合してしまって誤認の可能性があると感じました。 withとapplyの"this"を省略することもできますが、レシーバのメンバと外部のオブジェクトや関数との区別がつきにくくなる可能性もあります。 よって、withとapplyは使わないことに決めました。 また、letとalsoはデフォルトでは"it"でオブジェクトに参照できますが、カスタム名をつけることができるのでスコープの外部と内部を区別しやすくなります。

fun getRandomInt(): Int {
    return Random.nextInt(100).also { value ->
        // カスタム名"value"でオブジェクトを参照することができます。
        writeToLog("getRandomInt() generated value $value")
    }
}

val i = getRandomInt()

runは?

runもwith、apply同様にオブジェクトへの参照は"this"です。 以下のような場合に使うのを許可しています。

エルビス演算子と併用することによって、sample()の戻り値がnullだった場合にrunのスコープ内の処理を実行することができます。 またこの場合、runのレシーバーはクラスになるのでスコープの外と内で"this"は両方ともクラスを参照することになるので誤認の可能性はなくなります。

class Foo {
    fun bar() {
        sample()?.let {
              // sample()の戻り値がnotnullだった場合の処理
         } ?: run {
             // sample()の戻り値がnullだった場合の処理
         }
    }
}

現場からは以上です。