拡張機能がありますが、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()}
レシーバーを備えたラムダは基本的に拡張関数とまったく同じであり、プロパティに格納して関数に渡すことができます。この質問は、「関数がある場合のラムダの目的は何ですか?」と本質的に同じです。答えもほとんど同じです。コード内のどこにでも匿名の拡張関数をすばやく作成できます。
これには多くの良い使用例がありますが(特に 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
インスタンスは、ユーザーが管理することはありません。関数を作成して拡張関数を呼び出すのは、関数の内部実装次第です。StringBuilder
で1回呼び出すだけでなく、さまざまなStringBuilder
インスタンスで複数回呼び出すこともできます。後で使用するためなどに保管してください。拡張機能は、ある意味で受信機を備えた機能です。レシーバーでラムダを使用している場合、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
}
_
同様の方法で、レシーバーを使用して独自のラムダを定義できます。