web-dev-qa-db-ja.com

レシーバー付きラムダの目的は何ですか?

拡張機能がありますが、KotlinのReceiverを使用したLambdaの目的は何ですか?

以下の2つの関数は同じことを行いますが、最初の関数はより読みやすく短いものです。

fun main(args: Array<String>) {
    println("123".represents(123))
    println(123.represents("123"))
}

fun String.represents(another: Int) = toIntOrNull() == another

val represents: Int.(String) -> Boolean = {this == it.toIntOrNull()}
16
uptoyou

レシーバーを備えたラムダは基本的に拡張関数とまったく同じであり、プロパティに格納して関数に渡すことができます。この質問は、「関数がある場合のラムダの目的は何ですか?」と本質的に同じです。答えもほとんど同じです。コード内のどこにでも匿名の拡張関数をすばやく作成できます。

これには多くの良い使用例がありますが(特に DSL を参照)、ここで1つの簡単な例を示します。

たとえば、次のような関数があるとします。

fun buildString(actions: StringBuilder.() -> Unit): String {
    val builder = StringBuilder()
    builder.actions()
    return builder.toString()
}

この関数の呼び出しは次のようになります。

val str = buildString {
    append("Hello")
    append(" ")
    append("world")
}

この言語機能が有効にした興味深いことがいくつかあります。

  • buildStringに渡すラムダ内では、新しいスコープにいるため、新しいメソッドとプロパティを使用できます。この特定のケースでは、インスタンスでメソッドを呼び出すことなく、StringBuilderタイプのメソッドを使用できます。
  • これらの関数呼び出しが行われる実際のStringBuilderインスタンスは、ユーザーが管理することはありません。関数を作成して拡張関数を呼び出すのは、関数の内部実装次第です。
  • その結果、この関数は、渡したラムダを1つのStringBuilderで1回呼び出すだけでなく、さまざまなStringBuilderインスタンスで複数回呼び出すこともできます。後で使用するためなどに保管してください。
28
zsmb13

類似性

拡張機能は、ある意味で受信機を備えた機能です。レシーバーでラムダを使用している場合、Kotlinの拡張機能機能を利用しています。

lambdaは、通常の関数と同様の動作を定義する方法です。

レシーバー付きラムダは、拡張関数と同様の動作を定義する方法です。

レシーバーを使用したラムダの目的を理解するために、Buttonを作成して返す次の関数例を検討してください。

_fun createButton(): Button {
    val button = Button()
    button.text = "Some text"
    button.height = 40
    button.width = 60
    button.setOnClickListener(listener)
    button.background = drawable
    return button
}
_

上記のように、buttonオブジェクトでさまざまなメソッドを呼び出し、呼び出しごとにbuttonという名前を繰り返します。これはほんの一例です。式が長くなったり、何度も繰り返されたりすると、不便で見栄えが悪くなります。


目的

より簡潔で、きれいで、読みやすくするために、拡張関数apply()を使用して受信機付きのラムダを使用します。そして、上記のコードを次のようにリファクタリングします。

_fun createButton() = Button().apply {
    text = "Some text"
    height = 40
    width = 60
    setOnClickListener(listener)
    background = drawable
}
_

これで、コードがより見やすくなりました。 Button()はレシーバーオブジェクトであり、メソッドを呼び出してプロパティを設定できます。

これは、インスタンスを作成し、いくつかのプロパティを即座に初期化する場合に役立ちます。 Javaでは、これはBuilderパターンを使用して行われます。 Kotlinでは、Builderパターンをサポートしていない場合でも、任意のオブジェクトでapply()を使用できます。

apply()関数は、Kotlin標準ライブラリで次のように定義されています(簡略化)。

_fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}
_

同様の方法で、レシーバーを使用して独自のラムダを定義できます。

1