Inside a receiver lambda, what does this refer to, and how do you reach an outer receiver or the lambda's own label? Explain this@Outer and the difference from named-parameter access.
answer
- Bare this = innermost implicit receiver
- Nesting shadows; outer reached via this@Label
- Label = enclosing function or class name
- Parameter lambdas use it/name, no this@
- @DslMarker blocks only unqualified outer access
basics
~20 sInside the block, this is the receiver the lambda was invoked on. When nested inside another receiver block, you reach the outer one with a label, like this@Outer. A plain parameter is reached by its name or it instead.
solid answer
~50 sIn a lambda of type `A.() -> Unit`, the unlabeled `this` is the innermost implicit receiver — the `A` it was invoked on. When receiver blocks nest (e.g. `outer { inner { ... } }`), the inner block's `this` shadows the outer; you reach the outer receiver with a **qualified this** using its label: `this@FunctionName` or `this@ClassName`. Each builder function/class contributes a labeled receiver. By contrast, parameter lambdas expose the value as `it` or a chosen name, which is just a normal variable and never needs `this@`. The label after `this@` is the name of the enclosing function (for receiver-lambda parameters of named functions) or the enclosing class/extension — chosen by the compiler from the declaration site. @DslMarker exists precisely because unqualified access can accidentally hit an outer receiver; it restricts that, but qualified `this@Outer` still works.
code
kotlin · 12 linesclass Html { val tag = "html"; fun body(block: Body.() -> Unit) = Body().block() }
class Body { val tag = "body" }
fun html(block: Html.() -> Unit) = Html().block()
html { // this: Html
println(tag) // "html"
body { // this: Body (shadows Html)
println(tag) // "body"
println(this@html.tag) // "html" via qualified this
}
}go deeper
Knows this is the receiver object inside the block.
Understands nesting shadows and that outer is reached via this@Label.
Explains label derivation, shadowing semantics, and contrasts with parameter lambdas.
Connects receiver labeling to DSL design and the rationale for @DslMarker without conflating the mechanisms.
## What this is inside the block Within `A.() -> Unit`, the bare `this` is the **implicit receiver of type A** — the object the lambda was called against. You normally never write `this` because members are reachable unqualified; you write it only to pass the whole object or to disambiguate. ## Nesting shadows the receiver When receiver blocks nest, each introduces a **new implicit receiver** that shadows the outer one for unqualified names: ```kotlin class Outer { fun outerOp() {} ; fun inner(block: Inner.() -> Unit) = Inner().block() } class Inner { fun innerOp() {} } fun build(block: Outer.() -> Unit) = Outer().block() build { // this: Outer inner { // this: Inner (shadows Outer) innerOp() // this.innerOp() -> Inner.innerOp // outerOp() // still reachable unqualified UNLESS blocked by @DslMarker } } ``` ## Reaching the outer receiver: qualified this Use **`this@Label`** to name a specific enclosing receiver. The label is the **enclosing function or class name**: ```kotlin class Outer { fun build(block: Outer.() -> Unit) = block() fun config() { // inside some Inner.() block nested here you could write this@Outer } } fun Outer.dsl(block: Inner.() -> Unit) { Inner().block() } ``` For a receiver-lambda parameter of a named function `fun build(block: Outer.() -> Unit)`, inside the block the receiver is labeled by the function: `this@build` (the function name) — more commonly you use the type/class label `this@Outer` when the receiver is a class with that name in scope. The exact label is the **name at the declaration site**; the IDE shows it. The point: qualified `this@X` selects which implicit receiver you mean. ## Versus named-parameter access A **parameter lambda** value is just a local variable (`it` or a name you give it). It does **not** participate in `this@` labeling and never shadows other receivers — you simply use the variable. That clarity is one reason to choose `(A) -> Unit` when you don't want DSL-style scoping. ## Relationship to @DslMarker (boundary) This question is about reaching receivers; **@DslMarker** (a sibling topic) changes only **unqualified** resolution by forbidding implicit access to an outer same-marked receiver. Qualified `this@Outer` still works under @DslMarker. Don't conflate the two: receiver labeling is the language mechanism; @DslMarker is an opt-in restriction layered on top. ## Keywords/APIs - Qualified `this@Label` (label = enclosing function/class name). - Implicit receiver shadowing in nested receiver lambdas. - `it` / named parameter for parameter lambdas.
- What is the label after this@ derived from?The name of the enclosing function or class that declared the implicit receiver in scope.
- Does @DslMarker stop you from using this@Outer?No. @DslMarker only forbids implicit (unqualified) access to an outer same-marked receiver; qualified this@Outer still works.
saying these in an interview costs you the question
- Says nested receivers don't shadow
- Claims you reach an outer receiver with it
- Confuses this@Label with @DslMarker enforcement
- Thinks parameter lambdas need this@ labeling