Mastering Kotlin's Scope Functions: let, run, with, apply, and also 🎯

Jimmy McBride - Apr 24 '23 - - Dev Community

Kotlin is well-known for its elegant and concise syntax that improves the overall development experience. One of the powerful features Kotlin brings to the table is scope functions. They allow you to streamline your code, make it more readable, and simplify complex operations. In this blog post, we'll explore the five main scope functions in Kotlin: let, run, with, apply, and also. Buckle up and let's dive in! 🚀

Let

let is an extension function that executes a block of code within the context of the object it's called on. It returns the result of the last expression in the block. It's particularly useful when you want to perform operations on a nullable object, as it enables you to work within a safe, non-nullable context.

val nullableString: String? = "Kotlin is awesome!"
nullableString?.let { 
    println(it.length) // Prints the length of the non-null string
}
Enter fullscreen mode Exit fullscreen mode

Run

run is similar to let, but it operates within the context of this instead of using it. It's perfect for executing multiple operations on the same object or working with mutable properties. Like let, run also returns the result of the last expression in the block.

val result = "Hello, world!".run {
    this.toUpperCase()
    this.reversed()
}
println(result) // Prints "!dlrow ,olleH"
Enter fullscreen mode Exit fullscreen mode

With

with is a non-extension function that receives an object as its first parameter and a block of code as its second parameter. It allows you to work within the object's context and returns the result of the last expression in the block.

data class Person(val name: String, val age: Int)

val person = Person("John", 30)
val info = with(person) {
    "Name: $name, Age: $age"
}
println(info) // Prints "Name: John, Age: 30"
Enter fullscreen mode Exit fullscreen mode

Pro Tip

with and run are very similar. The main difference being that run is a dot method and with being a higher-order function. Having run be a dot method is actually great with dealing with nullable values!

data class Person(val name: String, val age: Int)

val person: Person? = Person("John", 30)

val info = with(person) {
    // this will give us issues because person might be null
    "Name: $name, Age: $age"
    // to fix it, we'll have to do this
    "Name: ${this?.name}, Age: ${this?.age}"
    // but the above seems kind of verbose
}
// we can use a safe call before run to clean up our code
val info = person?.run {
    "Name: $name, Age: $age"
}
Enter fullscreen mode Exit fullscreen mode

Apply

apply is another extension function that operates within the context of the calling object using this. It's handy for configuring or initializing objects, as it returns the object itself after executing the block of code.

data class User(var name: String, var email: String)

val user = User("Jane", "jane@example.com").apply {
    name = "Jane Doe"
    email = "jane.doe@example.com"
}
println(user) // Prints "User(name=Jane Doe, email=jane.doe@example.com)"
Enter fullscreen mode Exit fullscreen mode

Also

also is similar to apply, but it uses it instead of this within the block. It's useful for performing side effects or logging without modifying the object. Like apply, it also returns the object itself.

val numbers = mutableListOf(1, 2, 3, 4, 5).also {
    println("Original list: $it")
    it.add(6)
}
println("Updated list: $numbers")
Enter fullscreen mode Exit fullscreen mode

Kotlin's scope functions are powerful tools that can simplify your code, make it more readable, and improve your development experience. By mastering let, run, with, apply, and also, you'll be well on your way to becoming a Kotlin expert. Keep exploring and have fun coding! 🌟

Don't forget to follow for more Kotlin insights and tips!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .