Kotlin’s scope functions (let
, run
, with
, apply
, and also
) are powerful tools that help you write more concise and expressive code. They allow you to perform operations on objects within a temporary scope, making your code cleaner and more readable.
What Are Scope Functions?
Scope functions are extension functions that execute a block of code on an object and return a result (either the object itself or a computed value). They differ in:
- Receiver (
this
vs.it
) - Return value (object vs. result of lambda)
The 5 Scope Functions
Here’s a quick comparison:
Function | Receiver (this /it ) | Return Value | Common Use Case |
---|---|---|---|
let | it (explicit) | Lambda result | Null checks, transformations |
run | this (implicit) | Lambda result | Object initialization + computation |
with | this (implicit) | Lambda result | Grouping operations on an object |
apply | this (implicit) | Object itself | Configuring objects |
also | it (explicit) | Object itself | Side effects, logging |
1. let
– Safe Calls & Transformations
Use Case: Null checks and transformations.
Example: Safe Call on Nullable Object
val name: String? = "Kotlin"
name?.let {
println(it.uppercase()) // "KOTLIN"
}
it
refers to the object (name
).- Returns the result of the lambda.
Example: Chaining Operations
val numbers = listOf(1, 2, 3)
numbers
.filter { it > 1 }
.let { filtered -> println(filtered) } // [2, 3]
2. run
– Object Initialization & Computation
Use Case: Running operations on an object and returning a result.
Example: Compute a Value
val result = "Kotlin".run {
println(this.length) // 6
this.uppercase() // Returns "KOTLIN"
}
this
refers to the receiver ("Kotlin"
).- Returns the lambda result.
Example: Initialize & Compute
val user = User().run {
name = "Alice"
age = 25
this // Returns the configured User object
}
3. with
– Grouping Operations
Use Case: Performing multiple operations on an object without chaining.
Example: Modifying a List
val numbers = mutableListOf(1, 2, 3)
with(numbers) {
add(4)
remove(2)
println(this) // [1, 3, 4]
}
this
refers tonumbers
.- Returns the lambda result.
4. apply
– Object Configuration
Use Case: Initializing or configuring objects (builder pattern).
Example: Configuring a View
val button = Button(context).apply {
text = "Click Me"
textSize = 16f
setOnClickListener { /* ... */ }
}
this
refers to theButton
.- Returns the object itself (
button
).
5. also
– Side Effects & Logging
Use Case: Performing additional actions without altering the object.
Example: Logging
val list = listOf(1, 2, 3)
.also { println("Original list: $it") } // Logs before mapping
.map { it * 2 }
it
refers to the list.- Returns the original object.
When to Use Which?
Scenario | Recommended Function |
---|---|
Null checks | let |
Transforming data | let , run |
Object configuration | apply |
Side effects (logging) | also |
Grouping operations | with |
Key Takeaways
let
→ Safe calls, transformations.run
→ Compute a value from an object.with
→ Group operations on an object.apply
→ Configure objects (builder pattern).also
→ Side effects (logging, debugging).
Scope functions make Kotlin code more concise and expressive. Use them wisely to improve readability! 🚀