Scope関数

kotlinlang.org

Scope functions

標準ライブラリに含まれているスコープ関数。 スコープ関数の目的は、一つのオブジェクトのコンテキストを内含するコードブロックを実行することである。

  • let
  • run
  • with
  • apply
  • also

スコープ関数を使うことで、より正確にリーダブルに書くことができるようになる

letで書いた場合

Person("Alice", 20, "Amesterdam").let {
    println(it)
    it.moveTo("London")
    it.incrementAge()
    println(it)
}

letを使わない場合

val alice = Person("Alice", 20, "Amsterdam")
println(alice)
alice.moveTo("London")
alice.incrementAge()
println(alice)

関数の選択

Function selection

Function Object reference 返り値 拡張関数かどうか ユースケース
let it ラムダの結果 YES 関数のチェーン, アンラップ
run this ラムダの結果 YES objectの初期化とメソッドの実行
run - ラムダの結果 NO コンテキストオブジェクトを持たない property = run { 式 }
with this Contenxt object NO 引数にcontext objectを取る with(object) { 処理 }
apply this Context object YES objectの構成
also it Context object YES objectを使った処理
  • let 関数の選択
    • 非nullオブジェクトのラムダ式を実行する
    • ローカルスコープ内の変数として式を導入する
  • apply関数の選択
    • Objectの構成
  • run
    • Objectの構成と、結果の計算
  • non-extension run
    • 式が必要とされる箇所での文の実行
  • also
    • 追加的なエフェクト
  • with
    • Object上で関数呼び出しのグループ化

ただしオーバーラップするため、便利性と相談して決める

Functions

使い方のおすすめ

let

  • context object: it
  • 返り値: ラムダの結果

一つ以上の関数をチェインすることができる。

mutableListOf("one", "two", "three", "four", "five")
    .map { it.length }
    .filter { it > 3 }
    .let {
        println(it)
        // 更に必要となる関数を呼びだすことが可能
        // このラムダの結果が返り値となる
    }

式が一つの場合は、関数リテラルを渡すことも可能

mutableListOf("one", "two", "three", "four", "five")
    .map { it.length }
    .filter { it > 3 }
    .let (::println) // 関数オブジェクトを渡す

nullオブジェクトのアンラップ

val length = str?.let { // アンラップされる
    println("let() called on $it")
    processNonNullString(it)
    it.length
}

with

  • context object: this
  • 返り値: ラムダの結果

ラムダの結果を提供しないコンテキストオブジェクト上で関数を呼ぶために使用することをおすすめ。

withは、"with this object, do the following" と読むことができる (このobjectを使って、以下を実行する)

val numbers = mutableListOf("one", "two", "three")
with(numbers) {
    println("'with' は $this 引数を伴って呼ばれる")
    println("$size 要素を持つ")
}

他の例として、objectのプロパティや関数を使って値を計算するためのヘルパーオブジェクトを生成する。

val numbers = mutalbeListOf("one", "two", "three")
val firstAndLast = with(numbers) {
    "The first element is ${first()}, " +
    " the last element is ${last()}"
}

run

  • context object: this
  • 返り値: ラムダの結果

withと同じ目的を持つが、objectの拡張関数letと同じように呼び出す

runは、ラムダ内に、オブジェクトの初期化と返り値の計算をするのに便利に使える

val result = service.run {
    // objectのメンバの初期化やメソッドを呼び出す
    port = 8080
    query(prepareRequest())
}

letで書いた場合

val result = service.let {
    it.port = 8080
    it.query(it.prepareRequest())
}

non-extension run

複数の文を、一つの式にまとめる

class App {
    val hexNumberRegex = run { // 初期化時に内部が実行される
        val digits  ="0-9"
        val hexDigits = "A-Fa-f"
        val sign = "+-"
        Regex("[$sign]?[$digits$hexDigits]+")
    }
}

// 出力: [+-]?[0-9A-Fa-f]+
println(App().hexNumberRegex

runでまとめなかった場合

class App {
    val hexNumberRegex = { // 初期化時に内部は実行されない
        val digits  ="0-9"
        val hexDigits = "A-Fa-f"
        val sign = "+-"
        Regex("[$sign]?[$digits$hexDigits]+")
    }
}

// 関数が出力される:  Function0<kotlin.text.Regex>
println(App().hexNumberRegex

apply

  • context object: this
  • 返り値: object

objectの構成で使用する。

"apply the following assignments to the object"と読める (以下の代入をobjectに適用する)

val adam = Person("Adam").apply {
    // objectの構成
    age = 32
    city = "London"
}

also

  • context object: it
  • 返り値: object

"and also do the following with object." またobjectを使って以下を実行する

numbers
    .also { 
        // objectを使った処理
        println("The last elements before adding new one: $it")
    }
    .add("four")