A lambda with receiver gives an object a special role inside a lambda body: its members can be called without qualifying the object. This makes repeated work on one object more compact and gives the block a context distinct from surrounding code. Kotlin uses the pattern in standard functions such as with, apply, and buildString; Java has no equivalent receiver-lambda feature. section 5.4 section 5.5
Build a string without repeating its receiver
```kotlin fun alphabet() = buildString { for (letter in 'A'..'Z') append(letter) append("\nNow I know the alphabet!") } ``` The `buildString` block has a `StringBuilder` receiver. `append` is therefore shorthand for `this.append`; writing `this` remains useful when disambiguation is needed. A comparable ordinary lambda would require an explicit parameter such as `it.append(...)` on every call. section 13.2.1 section 5.4.1
Choose the standard scope function by its return value and intended operation:
- `with(receiver) { ... }` makes `receiver` implicit and returns the lambda's last expression, fitting several operations whose computed result matters. section 5.4.1
- `object.apply { ... }` makes the object implicit and returns that initialized object, fitting builder-style configuration. section 5.4.2
- `object.also { ... }` returns the object but passes it as `it`; it is for additional effects, not a lambda with an implicit receiver. section 5.4.3
The type syntax is an extension function type. `StringBuilder.() -> Unit` means a function that runs with a `StringBuilder` receiver, whereas `(StringBuilder) -> Unit` supplies a normal explicit parameter. A caller invokes such a lambda on a receiver instance, as in `sb.builderAction()`. Receiver lambdas can also be stored in variables and invoked on compatible objects. section 13.2.1
Listing 13.2 Redefining buildString to take a lambda with a receiver
fun buildString(
builderAction: StringBuilder.() -> Unit #1
): String {
val sb = StringBuilder()
sb.builderAction() #2
return sb.toString()
}
fun main() {
val s = buildString {
this.append("Hello, ") #3
append("World!") #4
}
println(s)
// Hello, World!
}
Compare an ordinary `StringBuilder` lambda using `it.append` with a receiver lambda using `append` directly, including the corresponding function declaration and invocation. section 13.2.1
Nested receiver lambdas underpin structured Kotlin DSLs. In an HTML builder, each block contributes names resolved from its receiver type: `table` can expose `tr`, and `tr` can expose `td`. That structure can enforce a grammar through the API, but several receivers in scope can make an unqualified call confusing; labeled `this` can make the target explicit, and `@DslMarker` can constrain accidental access to outer receivers. section 13.2.2
Define a receiver-lambda builder
```kotlin fun buildString(builderAction: StringBuilder.() -> Unit): String { val sb = StringBuilder() sb.builderAction() return sb.toString() } ``` Here `sb` becomes the implicit `this` while `builderAction` runs. The same mechanism lets an `apply`-style helper invoke a block and then return its receiver. section 13.2.1
Use receiver lambdas when a block primarily configures, builds, or navigates one context object. They support concise object initialization, mutable construction of read-only collections, and expressive DSL blocks for markup, build dependencies, tests, and database APIs. The compactness is most valuable when the receiver is obvious; qualify or label calls when nested contexts would obscure which object owns a member. section 5.4.2 section 13.3.2
