Understanding Scope Functions in Kotlin

Understanding Scope Functions in Kotlin


Scope Functions – Kotlin is a multiparadigm programming language. This means that besides being object-oriented, its syntax also supports functional programming styles.

Kotlin Logo

Let’s explore the core scope functions in Kotlin:

run

The run function returns a value based on the expression within the lambda block. It accesses the object’s context via the receiver this. It’s particularly useful for performing object initialization and computing a return value simultaneously.

Example:

fun getNullableLength(ns: String?) {
    println("for \"$ns\":")
    ns?.run {
        println("\tis empty? " + isEmpty())
        println("\tlength = $length")
        length
    }
}

getNullableLength(null)
getNullableLength("")
getNullableLength("some string with Kotlin")
  1. Calls the block on a nullable variable.
  2. Inside run, the object is accessed without its name.
  3. Returns the length of the string if it’s not null.

with

Unlike others, with is not an extension function; it’s a standard function where the object context is passed as an argument and accessed as a receiver inside the lambda.

Example:

with(configuration) {
    println("$host:$port")
}

// Instead of:
println("${configuration.host}:${configuration.port}")

apply

The apply function returns the object context itself. The context is available as a receiver (this). It’s ideal for configuring or initializing an object.

Example:

val jake = Person().apply {
    name = "Jake"
    age = 30
    about = "Android Developer"
}.toString()
  • Creates an instance of Person with default values.
  • Applies the code block to the instance.
  • Inside apply, name = "Jake" is equivalent to jake.name = "Jake".
  • The return value is the initialized instance itself, allowing for operation chaining.

let

The let function uses the argument it to access the object context. It is frequently used for performing operations on non-null objects.

Example:

val empty = "test".let {
    customPrint(it)
    it.isEmpty()
}
println("is empty: $empty")

fun printNonNull(str: String?) {
    println("Printing \"str\":")
    str?.let {
        print("\t")
        customPrint(it)
        println()
    }
}

printNonNull(null)
printNonNull("my string")
  • Executes the block on the result of the “test” string.
  • Accesses the string using it.
  • Returns the result of the expression inside the block.
  • The safe call ?. ensures the block only runs if the value is not null.

also

Similar to apply, also returns the object context. However, the context is available as an argument (it). Use it when you want to perform actions with the object context without modifying the object itself (like logging).

Example:

val jake = Person("Anwar", 25, "Android developer").also {
    writeCreationLog(it)
}
  • Creates a Person object.
  • Applies the specified code block. The return value is the object itself.
  • Calls a logging function passing the object as an argument.