You see new code using also { } to initialize fields and apply { } to register the object in a global registry. Critique these choices and propose the idiomatic split.
answer
- apply = configure own fields (this)
- also = external side effect (it): log/register
- The reviewed code inverted the two
- Wrong this risks member-name collision
- Prefer constructor/init for own invariants
basics
~10 sThe two are swapped. apply (this) is for configuring the object's own fields; also (it) is for side effects like registering it somewhere. Flip them so the code reads as intended.
solid answer
~50 sThe choices invert the idiomatic convention. apply exposes the object as this and is meant for configuration — setting the object's own properties. also exposes it as it and is meant for side effects on the outside world — like adding the object to a global registry. The reviewed code uses also to set fields (forcing it.field everywhere, noisier than apply) and apply to register (using this, which obscures that the registry is a separate object and risks member-name collisions). The idiomatic split: field initialization belongs in apply { field = ... }; the registry side effect belongs in also { registry += it }. Both still return the receiver, so behavior is identical — but matching function to intent makes the code self-documenting and avoids subtle this-shadowing bugs. For trivial cases either works; the value is communicating intent at a glance.
go deeper
May only spot that this vs it differ, without the intent argument.
Identifies the inversion and proposes apply-for-config / also-for-side-effect.
Adds the this-shadowing collision risk and notes constructor/init as a better tool for owned invariants.
Frames it as a team readability/convention decision, weighs chain depth vs plain statements, and sets guidance for interop vs owned types.
## Restating the convention The Kotlin community convention maps function to **intent**, not just mechanics: - **`apply`** → *configure the receiver itself* (its own properties/setters). Receiver is `this`, so member access is terse. - **`also`** → *do something on the side with the object* (log, validate, register, cache). Object is `it`, signaling "separate action." Both return the receiver, so they're interchangeable for *correctness*. The point is **readability and intent**. ## The code under review ```kotlin // As written (anti-idiomatic): val svc = Service() .also { it.name = "auth" // configuring fields via `it.` — noisy it.timeout = 5000 } .apply { registry.register(this) // side effect via `this` — misleading } ``` Problems: 1. **`also` for field init** forces `it.` on every assignment; `apply` would let you drop it. 2. **`apply` for registration** uses `this`, implying you're configuring the Service, when really you're touching an external `registry`. The bare `register(...)` could even collide with a hypothetical `Service.register` member, calling the wrong thing. 3. Reader has to slow down to realize intent is inverted. ## The idiomatic rewrite ```kotlin val svc = Service().apply { name = "auth" // configure: this.name timeout = 5000 }.also { registry.register(it) // side effect on external registry } ``` Now `apply` clearly says "set up the Service" and `also` clearly says "register it externally." Same returned value (`svc` is the Service), zero behavior change, much clearer. ## Deeper points a strong candidate raises - **this-shadowing risk.** Inside `apply`, an unqualified call resolves to a member of the receiver first. If the external API name matches a member, you get the wrong target silently. `also`'s explicit `it`/named param removes that risk for external calls. - **Init blocks vs apply.** If these are the object's *own* invariants, a constructor with default/named args or an `init {}` block may beat post-construction `apply` entirely — `apply` shines mainly for Java-interop mutable types you can't change. - **Don't over-chain.** Long `apply().also().apply()` ladders can hide control flow; sometimes plain statements read better. ## Verdict Swap them: `apply` for the field configuration, `also` for the registry side effect.
- Is there any behavioral difference after swapping them?No. Both apply and also return the receiver and run their blocks eagerly; only readability and collision-safety change.
- When would you not use apply at all for field init?When you own the class — a constructor with named/default parameters or an init block expresses invariants better; apply is mainly for mutable Java-interop types.
saying these in an interview costs you the question
- Defending also for field configuration as equally idiomatic with no caveats
- Not noticing the this-shadowing/collision risk in apply for external calls
- Claiming the swap changes behavior
- Missing that a constructor/init may obviate apply entirely