Scope function

Kotlin provides a set of scoped functions that execute code blocks in the context of temporary scoped objects created by functions. There are five scoped functions: let, with, run, apply, and also.

  • Use of object context in scope domain (this or it)
  • Return result of scoped function (context object or Lambda expression result)

let

Let is often used for non-null-value execution of code blocks, with the security operator? Combined into? .let performs safe non-null code operations:

  • Object context: it
  • Return value: result of the lambda expression
fun main(args: Array<String>) {
    val list = listOf(1.2.3.4.5.6) list? .let { it -> println(it[0]) / / 1
        it.forEach(::println) // 1 2 3 4 5 6
    }

    val ele1 = list.let { it[5] }.let { it * 2 }
    println(ele1) / / 12
}
Copy the code

Another scenario where let is often used is when you don’t bother naming variables and use it instead.

with

With is not an extension function, so it cannot be executed as an object. With receives two arguments. Parameter 1 is the context object, which can be used in temporary scopes instead of this:

  • Object context: this
  • Return value: result of the lambda expression
fun main(args: Array<String>) {
    val list = listOf(1.2.3.4.5.6)
    with(list) {
        println(this[0]) / / 1
        // this.forEach(::println) // this can be omitted
        forEach(::println) // 1 2 3 4 5 6
    }

    val ele1 = with(list) { this[5] }
    println(ele1) / / 6
}
Copy the code

For this object, do the following.

run

Run is an extension of the with function, which is exactly the same as with, except that it is called differently:

  • Object context: this
  • Return value: result of the lambda expression
fun main(args: Array<String>) {
    val list = listOf(1.2.3.4.5.6)
    list.run {
        println(this[0]) / / 1
        // this.forEach(::println) // this can be omitted
        forEach(::println) // 1 2 3 4 5 6
    }

    val ele1 = list.run { this[5] }
    println(ele1) / / 6
}
Copy the code

However, there is also a non-extension version of run, which acts a bit like a pure block of code, minus an object context:

fun main(args: Array<String>) {
    val list = listOf(1.2.3.4.5.6)
    run {
        // No context
        list.forEach { println(it) } // 1 2 3 4 5 6
    }
    val ele1 = run { list[5] }
    println(ele1) / / 6
}
Copy the code

apply

Apply receives and eventually returns object context and is often used to configure objects:

  • Object context: this
  • Return value: context object
fun main(args: Array<String>) {
    val list = mutableListOf(1.2.3.4.5.6)
    list.apply {
        this.add(0.0)
    }.apply {
        add(0, -1)
    }

    list.forEach(::println) // -1 0 1 2 3 4 5 6
}
Copy the code

For this object, do the following configuration.

also

Also is useful for performing operations that take context objects as arguments. Use also for operations that need to refer to an object rather than its properties and functions, or when you don’t want to mask this references from an external scope.

  • Object context: it
  • Return value: context object
fun main(args: Array<String>) {
    val list = mutableListOf(1.2.3.4.5.6)
    list.also {
        it.add(0.0)
    }.also {
        it.add(0, -1)
    }
    list.forEach(::println) // -1 0 1 2 3 4 5 6
}
Copy the code

conclusion

The purpose of scoped functions is the same, but there are some subtle differences in the way they are used:

  • Scope: Generates a temporary scope and operates on the object context in that scope.
  • Difference: The use of the object context in the temporary scope (this or it), and the return value of the scope (object context or Lambda expression result).

These subtle differences can be confusing, but the summary table from Kotlin’s official documentation can help clarify the differences visually:

function Object reference The return value Extension function
let it Lambda expression result is
run this Lambda expression result is
run Lambda expression result No: Call context-free objects
with this Lambda expression result Not: take the context object as an argument
apply this Context object is
also it Context object is

Here is a short guide to selecting scope functions for their intended purpose:

  • Executes a lambda expression on a non-null object: let
  • The expression is introduced as a variable into the local scope: let
  • Object configuration: apply
  • Object configuration and computes the result: run
  • Run statements where expressions are needed: non-extended RUN
  • Additional effect: also
  • A set of function calls to an object: with

Some references from Kotlin official document: www.kotlincn.net/docs/refere…