let, run, also, apply, with
In this page:
val r1 = receiver.let { /* it -> */ result }
val r2 = receiver.run { /* this -> */ result }
val r3 = receiver.also { /* it -> */ } // returns receiver
val r4 = receiver.apply { /* this -> */ } // returns receiver
val r5 = with(receiver) { /* this -> */ result }
let for Null Checks and Transformations
let executes a block with the receiver available as it and returns whatever the block produces, and is commonly combined with ?. to run code only when a nullable value is present.
Example: let for Null Checks and Transformations
// Entry point: execution of the program starts here
fun main() {
// Declare a read-only variable `name` (a `String?`), set to `"Kotlin"`
val name: String? = "Kotlin"
// Call `name?.let` with a trailing lambda, storing the result in `length`
val length = name?.let {
// Print "Name is not null: $it" to the console, with a trailing newline
println("Name is not null: $it")
it.length
}
// Print "Length: $length" to the console, with a trailing newline
println("Length: $length")
}
Login to try C/C++/Java/PHP code in the editor
run for Grouped Computation
run executes a block with the receiver available as this, returning the block's result -- useful for computing a value from several steps involving that receiver's members directly.
Example: run for Grouped Computation
// Define a class `Config`
class Config(var name: String = "", var port: Int = 8080)
// Entry point: execution of the program starts here
fun main() {
// Call `Config().run` with a trailing lambda, storing the result in `summary`
val summary = Config().run {
name = "Server"
port = 9090
"$name running on port $port"
}
// Print `summary` to the console, with a trailing newline
println(summary)
}
Login to try C/C++/Java/PHP code in the editor
also for Side Effects
also runs a block with the receiver as it but always returns the original receiver unchanged, which makes it ideal for logging or debugging in the middle of a chain of calls.
Example: also for Side Effects
// Entry point: execution of the program starts here
fun main() {
// Declare a read-only variable `numbers`, set to `mutableListOf(1, 2, 3)`
val numbers = mutableListOf(1, 2, 3)
.also { println("Original list: $it") }
// Add `4` to `numbers`
numbers.add(4)
// Print "After adding: $numbers" to the console, with a trailing newline
println("After adding: $numbers")
}
Login to try C/C++/Java/PHP code in the editor
apply for Configuring an Object
apply runs a block with the receiver as this and returns that same receiver, making it perfect for setting up several properties on a newly created object in one expression.
Example: apply for Configuring an Object
// Define a class `Person`
class Person {
// Declare a mutable variable `name` (a String), initialized to `""`
var name: String = ""
// Declare a mutable variable `age` (an Int), initialized to `0`
var age: Int = 0
}
// Entry point: execution of the program starts here
fun main() {
// Call `Person().apply` with a trailing lambda, storing the result in `person`
val person = Person().apply {
name = "Ana"
age = 30
}
// Print "${person.name}, ${person.age}" to the console, with a trailing newline
println("${person.name}, ${person.age}")
}
Login to try C/C++/Java/PHP code in the editor
with for Operating on an Existing Object
with(receiver) { ... } is not an extension function but a regular function that takes an object and a lambda where that object is available as this, returning the lambda's result -- useful when you already have the object in hand.
Example: with for Operating on an Existing Object
// Define a class `Rectangle`
class Rectangle(val width: Int, val height: Int)
// Entry point: execution of the program starts here
fun main() {
// Declare a read-only variable `rect`, set to `Rectangle(4, 5)`
val rect = Rectangle(4, 5)
// Call `with(rect)` with a trailing lambda, storing the result in `area`
val area = with(rect) {
width * height
}
// Print "Area: $area" to the console, with a trailing newline
println("Area: $area")
}
Login to try C/C++/Java/PHP code in the editor
- Mixing up
applyandalso;applyreturns the receiver itself (this) whilealsoalso returns the receiver but exposes it asit. - Mixing up
letandrun; both usethis/itdifferently and return the lambda's result, easy to misremember which uses which. - Overusing scope functions to chain many unrelated operations together, making the code harder to read than plain statements would be.
letruns a block with the value asitand returns the block's result -- often used with?.for null checks.runruns a block with the value asthisand returns the block's result.alsoruns a block with the value asitbut returns the original value -- good for side effects like logging.applyruns a block with the value asthisand returns the original value -- good for configuring an object.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: