Scope functions & extension functions
Scope functions (let, run, with, apply, also) open a temporary scope on an object. They differ along two axes: how the object is referenced (it vs this) and what is returned (the lambda result vs the object itself).
Extension functions "add" a function to an existing class without touching its code: fun String.toMasked(): String. Under the hood they are static utility functions — dispatched statically, not real members, with no access to private fields.
| Function | Object as | Returns | Typical use |
|---|---|---|---|
| `let` | `it` | Lambda result | Null checks: `x?.let { ... }` |
| `run` | `this` | Lambda result | Computing a value from an object |
| `with` | `this` | Lambda result | Many operations on one object |
| `apply` | `this` | The object itself | Object configuration (builder style) |
| `also` | `it` | The object itself | Side effects: logging, validation |
Practical rule: apply for configuration, let for null-safe transforms, also for side effects (logging). Avoid nesting scope functions — it becomes unclear which object each it refers to, and calling that out as an antipattern earns interview points.
🛠 Practice task
Take 3 snippets (from your own code or written fresh) and clean them up with scope functions:
- Object configuration with
apply, - A null check with
?.let, - A logging/analytics call with
also.
Then write a String.maskPan() extension and test it on "4169111122223333".
Done when: each change carries a note on why that particular function fits.
📚 Sources and documentation
- Scope functions — Kotlin docsofficialkotlinlang.org
- Extensions — Kotlin docsofficialkotlinlang.org