1. 概要

スコープ関数は非常に便利であり、Kotlinコードで頻繁に使用されます。

このチュートリアルでは、それらが何であるかを説明し、それぞれをいつ使用するかの例もいくつか示します。

2. また

まず、突然変異関数applyを見てみましょう。

簡単に言えば、ミューテーション関数は指定されたオブジェクトを操作してそれを返します。

も、拡張メソッドの場合、拡張オブジェクトを操作するラムダを提供します。

inline fun T.also(block: (T) -> Unit): T

呼び出されたオブジェクトが返されるため、コールチェーンでサイドロジックを生成する場合に便利です。

val headers = restClient
  .getResponse()
  .also { logger.info(it.toString()) }
  .getHeaders()

これは後で重要になるため、の使用に注意してください。

また、を使用してオブジェクトを初期化できます。

val aStudent = Student().also { it.name = "John" }

もちろん、インスタンスを it と呼ぶことができるので、名前を変更することもできます。多くの場合、より読みやすいものを作成します:

val aStudent = Student().also { newStudent -> newStudent.name = "John"}

確かに、ラムダに複雑なロジックが含まれている場合、インスタンスに名前を付けることができると、読者の助けになります。

3. 適用

ただし、itラムダパラメーターの余分な冗長性は必要ないかもしれません。

applyalsoと同じですが、暗黙のthisがあります。

inline fun T.apply(block: T.() -> Unit): T

オブジェクトを初期化するためにしたように、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")

ここでの主な違いは、それを使用しますが、を適用しません。

4. let

それでは、突然変異関数よりも一歩複雑な変換関数 let、run、withを見てみましょう。

簡単に言えば、変換関数はあるタイプのソースを取り、は別のタイプのターゲットを返します。

まず、 let:です

inline fun <T, R> T.let(block: (T) -> R): R

これは、ブロックがUnitではなくRを返すことを除いて、とかなり似ています。

これがどのように違いを生むか見てみましょう。

まず、 let を使用して、あるオブジェクトタイプから別のオブジェクトタイプに変換できます。たとえば、 StringBuilderを使用して、その長さを計算します。

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
}

または、次に、エルビス演算子を使用して条件付きで呼び出すことができ、デフォルト値を指定することもできます。

val message: String? = "hello there!"
val charactersInMessage = message?.let {
    "value was not null: $it"
} ?: "value was null"

letともとは異なります リターンタイプが変わるという点で。

5. 実行

run は、 apply にもに関連しているのと同じように、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 では、メッセージインスタンスをそれと呼びましたが、ここでは、メッセージはラムダ内の暗黙のthisです。

そして、null可能性を備えたletと同じアプローチを使用できます。

val message: String? = "hello there!"
val charactersInMessage = message?.run {
    "value was not null: $this"
} ?: "value was null"

6. with

最後の変換関数はと。 まるで走るそれは暗黙のこれ 、しかしそれは拡張メソッドではありません:

inline fun <T, R> with(receiver: T, block: T.() -> R): R

を使用して、オブジェクトをスコープに制限できます。 別の見方は、特定のオブジェクトへの複数の呼び出しを論理的にグループ化することです。

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

7. 結論

この記事では、さまざまなスコープ関数を調べ、それらを分類し、結果の観点から説明しました。 それらの使用法にはいくつかの重複がありますが、ある程度の実践と常識があれば、どのスコープ関数をいつ適用するかを学ぶことができます。

すべての例は、GitHubプロジェクトにあります。