Kotlin Scope関数

1. 概要

スコープ関数は非常に便利で、Kotlinコードで頻繁に使用します。
このチュートリアルでは、それらが何であるかを説明し、それぞれをいつ使用するかの例を提供します。

2. _その他

最初に、突然変異関数_also_と_apply_を見てみましょう。
簡単に言えば、*突然変異関数は与えられたオブジェクトに作用してそれを返します。*
拡張メソッド_also、_の場合、拡張オブジェクトを操作するラムダを提供します。
inline fun T.also(block: (T) -> Unit): T
呼び出されたオブジェクトを返します。これにより、コールチェーンでサイドロジックを生成するときに便利になります。
val headers = restClient
  .getResponse()
  .also { logger.info(it.toString()) }
  .getHeaders()
  • it *の使用に注意してください。これは後で重要になるためです。

    また、_also_を使用してオブジェクトを初期化できます。
val aStudent = Student().also { it.name = "John" }
もちろん、インスタンスを_it_として参照できるため、名前を変更して、*もっと読みやすいものを作成することもできます:*
val aStudent = Student().also { newStudent -> newStudent.name = "John"}
確かに、ラムダに複雑なロジックが含まれている場合、インスタンスに名前を付けることができると読者に役立ちます。

3. apply

しかし、*多分、_it_ *ラムダパラメーターの余分な冗長性は必要ありません。
__apply __は_also_に似ていますが、暗黙のthis_this_があります:
inline fun T.apply(block: T.() -> Unit): T
オブジェクトを初期化するために_also_したように_apply_を使用できます。 ただし、_it_は使用しないことに注意してください。
val aStudent = Student().apply {
    studentId = "1234567"
    name = "Mary"
    surname = "Smith"
}
または、ビルダースタイルのオブジェクトを簡単に作成するために使用できます。
data class Teacher(var id: Int = 0, var name: String = "", var surname: String = "") {
    fun id(anId: Int): Teacher = apply { id = anId }
    fun name(aName: String): Teacher = apply { name = aName }
    fun surname(aSurname: String): Teacher = apply { surname = aSurname }
}

val teacher = Teacher()
  .id(1000)
  .name("Martha")
  .surname("Spector")
*ここでの主な違いは、_also_は_it_を使用しますが、__apply __は使用しません。*

4. let

ここで、変換関数_let、run、_、_ with_を見てみましょう。これらは、突然変異関数よりも少し複雑です。
簡単に言えば、変換関数は1つのタイプのa__source_を受け取り、*別のタイプのa__target_を返します。*
まず、_let:_です
inline fun <T, R> T.let(block: (T) -> R): R
これは、ブロックが_Unit_ではなく_R_を返すことを除いて、_also_にかなり似ています。
これがどのように違いをもたらすかを見てみましょう。
まず、_let_を使用して、あるオブジェクトタイプから別のオブジェクトタイプに変換できます。
val stringBuilder = StringBuilder()
val numberOfCharacters = stringBuilder.let {
    it.append("This is a transformation function.")
    it.append
      ("It takes a StringBuilder instance and returns the number of characters in the generated String")
    it.length
}
または、Elvis演算子を使用して条件付きで呼び出し、デフォルト値を指定することもできます。
val message: String? = "hello there!"
val charactersInMessage = message?.let {
    "value was not null: $it"
} ?: "value was null"
  • let_は_also とは異なります 戻り値の型が変更されます。

5. run

  • _run_は、_apply_が_also_に関連するのと同じ方法で_let_に関連しています:*

inline fun <T, R> T.run(block: T.() -> R): R
_let_のような_R_型を返し、これを変換関数にしていることに注意してください。ただし、_apply._のような暗黙の_this、_を使用します。
微妙ですが、違いは例で明らかになります:
val message = StringBuilder()
val numberOfCharacters = message.run {
    append("This is a transformation function.")
    append("It takes a StringBuilder instance and returns the number of characters in the generated String")
    length
}
_let_では、* _ message_インスタンスを_it_と呼びましたが、ここでは、_message_はラムダ内の暗黙の_this_です*。
そして、null可能性を持つ_let_と同じアプローチを使用できます。
val message: String? = "hello there!"
val charactersInMessage = message?.run {
    "value was not null: $this"
} ?: "value was null"

6. with

最後の変換関数は__withです。 __暗黙の_this_を持つという点で_run_に似ていますが、拡張メソッドではありません:
inline fun <T, R> with(receiver: T, block: T.() -> R): R
  • _ with_を使用して、オブジェクトをスコープに制限できます。 **それを見る別の方法は、与えられたオブジェクトへの複数の呼び出しを論理的にグループ化することです:

with(bankAccount) {
    checkAuthorization(...)
    addPayee(...)
    makePayment(...)
}

7. 結論

この記事では、さまざまなスコープ関数を調査し、分類し、結果の観点から説明しました。 それらの使用法にはいくつかの重複がありますが、いくつかの実践と常識により、どのスコープ関数をいつ適用するかを学ぶことができます。
すべての例は、https://github.com/eugenp/tutorials/tree/master/core-kotlin-2 [GitHubプロジェクト]にあります。