What do Kotlin's apply { } and also { } return, and how does each give you access to the object inside the lambda?
answer
- Both return the receiver, not the lambda result
- apply = this, also = it
- apply configures, also side-effects/logs
- let/run return last expression instead
- All are inline functions
basics
~10 sBoth apply and also return the same object you called them on. Inside apply you use it as this; inside also you use it as it. Neither changes what value comes out.
solid answer
~40 sBoth apply and also are extension functions that return the receiver object itself (the thing before the dot), not the result of the lambda. The difference is how the lambda accesses that object: apply passes it as the lambda receiver (this), so you can call members directly without a name; also passes it as the single argument it. Because they return the original object, you can keep chaining method calls afterward. Use apply for configuring an object (set properties, call setters) and also for side effects like logging, validation, or adding to a list, where the it name keeps the original object readable. Compare with let/run, which return the lambda's last expression instead.
code
kotlin · 7 linesval person = Person().apply {
name = "Lena" // this.name
age = 30
}.also {
println("created: ${it.name}") // it == person
}
// person is fully configured, logged, and === to the apply/also resultgo deeper
Knows both return the object and that apply=this, also=it.
Explains why returning the receiver enables chaining and contrasts with let/run.
Mentions the inline/lambda-with-receiver signatures and picks the idiomatic function per use case.
Frames apply/also within the whole scope-function matrix (this/it x receiver-vs-lambda-result) and team readability conventions.
## The core fact `apply` and `also` are two of Kotlin's five **scope functions** (the others are `let`, `run`, and `with`). A *scope function* lets you run a block of code on an object inside a temporary scope. The thing you call the function on is the **receiver** — the object before the dot. The defining trait of `apply` and `also`: **they both return the receiver object itself**, not whatever the lambda evaluates to. This is what makes them ideal for fluent chains. ## How the object is exposed inside the lambda - **`apply`** uses a *lambda with receiver*. Inside the braces, the object is bound to **`this`**, so you can reference its members directly (often omitting `this`). Its signature is roughly `inline fun <T> T.apply(block: T.() -> Unit): T`. - **`also`** passes the object as a *regular argument* named **`it`**. Its signature is `inline fun <T> T.also(block: (T) -> Unit): T`. ```kotlin val list = mutableListOf(1, 2, 3) // apply: object is `this` val a = list.apply { add(4) // == this.add(4) add(5) } // returns the SAME list // also: object is `it` val b = list.also { println("size is ${it.size}") } // returns the SAME list println(a === b) // true — both are the original list ``` ## Why "returns the receiver" matters Because the original object comes back out, you can configure or peek at an object **in the middle of an expression** without breaking the chain: ```kotlin val user = User() .apply { name = "Ada" } // configure, get User back .also { log.info("built $it") } // log, get User back ``` Contrast: `let` and `run` return the **last expression** of the lambda, so they *transform* a value. `apply`/`also` never transform — they hand the object straight back. ## Quick mental model - `apply` → "configure this object" (`this`, returns object). - `also` → "do something on the side with `it`" (`it`, returns object). All four (`let`, `run`, `apply`, `also`) plus `with` are declared `inline`, so there is no lambda-allocation or call overhead at runtime.
- If you wrote `val x = list.apply { add(1) }`, what is x?x is the same list object (with 1 appended) — apply returns the receiver, not the result of add.
- Could you swap apply for also and keep `this.name = ...`?No. also exposes the object as it, not this, so you'd have to write it.name = ... instead.
apply is like handing a builder the object and saying 'set it up'; also is like passing it to an observer who notes something and hands it right back.
saying these in an interview costs you the question
- Saying apply returns the lambda's last expression (that's let/run)
- Claiming also uses this instead of it
- Thinking apply/also create a copy rather than returning the same instance
- Confusing apply with also's receiver style