skip to content

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.

level: seniorimportance: should knowfreq 35%

answer

  1. Bare this = innermost implicit receiver
  2. Nesting shadows; outer reached via this@Label
  3. Label = enclosing function or class name
  4. Parameter lambdas use it/name, no this@
  5. @DslMarker blocks only unqualified outer access

basics

~20 s

Inside 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 s

In 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 lines
kotlin
class 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

for a junior

Knows this is the receiver object inside the block.

for a middle

Understands nesting shadows and that outer is reached via this@Label.

for a senior

Explains label derivation, shadowing semantics, and contrasts with parameter lambdas.

for a principal

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

context