SoFunction
Updated on 2025-04-14

Kotlin Scope Functions Apply, let, run, with, also User Guide

In Kotlin development, Scope Functions are a set of higher-order functions that make the code more concise and functional. They solve common scenario problems such as object configuration, empty security processing, and chain operations through different scope rules and return value design. This article will combine core features, code examples and comparison tables to help you accurately master it.applyletrunwithalsoThe essence of use.

1. Introduction: Why are scoped functions needed?

In object-oriented programming, we often need to configure objects (such as setting properties), process null values, perform chain operations, or simplify member access. The traditional way may lead to code redundancy, while Kotlin's scope function passesScope limitationsandReturn value optimization, make these operations more elegant. For example:

// Traditional way: temporary variable + multiple callsval file = File("")
()
(true)
(true)
// Use apply to simplify:val file = File("").apply {
    createNewFile()
    setReadable(true)
    setWritable(true)
}

Next, we analyze the core mechanism and applicable scenarios of each function one by one.

2. Detailed explanation of scope functions

1. apply: "Stream Builder" for object configuration

  • Recipient Quotationthis(Can be omitted and directly call the receiver member)
  • Return value: The recipient object itself (T
  • Core uses: Initialize or configure the object, return the configured object
  • null safe: Not supported (receiver must not be null)
  • Scope: Recipient scope (thisPoint to the receiver)

Code example:

// Configure network request parametersval request = Request().apply {
    url = ""
    method = "GET"
    headers["Content-Type"] = "application/json"
}
// Simplify custom View initializationMyButton().apply {
    text = "submit"
    setOnClickListener { handleClick() }
    setBackgroundColor()
}

Best Practices:

  • Object construction: builder patterns that replace Java, such as(context).apply { ... }
  • Avoid temporary variables: Directly return to the configured object, chain calls are smoother

2. let: empty security and scope limitations

  • Recipient Quotationit(Implicit parameter, as the only parameter of lambda)
  • Return value: The execution result of lambda (any type)
  • Core uses: Process null values, limit the scope of variables, and return the new calculation result
  • null safe: Support (with security call characters?.
  • Scope: Independent scope (itOnly visible in lambda)

Code example:

// Safely handle nullable objectsval userName: String? = "Alice"
val greeting = userName?.let { "Hello, $it!" } ?: "Hello, Guest!"
// Limited scope to avoid variable contaminationval text = "Kotlin is great"
 { 
    val words = (" ")
    "Number of words:${}"  // it only works here}

Best Practices:

  • null verificationobj?.let { ... }Replace cumbersomeif (obj != null)
  • Temporary variables: Create temporary variables in lambda (such asval data = ()

3. run: "all-round player" in the receiver scope

  • Recipient Quotationthis(Can be omitted and directly call the receiver member)
  • Return value: The execution result of lambda (any type)
  • Core uses: Execute code blocks within the receiver scope, mix calls to member methods and external functions
  • null safe: Not supported (receiver null needs to be manually verified)
  • Scope: Receiver scope (priority access to recipient members)

Code example:

// Calculate the length of the file contentval file = File("")
val contentLength =  { 
    if (exists()) readText().length else 0
}
// Chain function call"Android".run {
    toUpperCase()       // Call the receiver method}.run { 
    "$this Kotlin"      // Handle intermediate results}.run(::println)      // Calling external functions(Print results)

Best Practices:

  • Member visits: Simplify receiver member calls (such as { setText("OK") }
  • Mixed logic: Use the receiver method at the same time (length) and external functions (println

4. With: parameterized variant of run

  • Recipient Quotation: Parameter passed in (non-extended function, directly using the receiver in lambda)
  • Return value: The execution result of lambda (same asrun
  • Core uses: Used in non-extended functionsrun, explicitly passed to the receiver
  • null safe: Not supported (receiver null needs to be manually verified)
  • Scope: Recipient scope (same asrun

Code example:

// explicitly passed into the receiver (non-extended function call)val result = with(ArrayList<String>()) {
    add("A")
    add("B")
    size  // Return lambda result}
// Mathematical computing scenarioval point = Point(3, 4)
val distance = with(point) { 
    sqrt(x*x + y*y)  // Direct access to the x/y attribute (assuming Point has x/y members)}

Best Practices:

  • Multi-object operation: When the receiver is not the call object (such aswith(list, ::process)
  • Alternative run: Used when used in parameterized calls (withrunThe functions are exactly the same)

5. Also: "Side Effects Keeper" of Chain Operation

  • Recipient Quotationit(Implicit parameter, as the only parameter of lambda)
  • Return value: The recipient object itself (T,sameapply
  • Core uses: Perform side effects operations (such as logs, assignments), and keep object chain calls
  • null safe: Not supported (receiver must not be null)
  • Scope: Independent scope (itOnly visible in lambda) Code example:
// Logging and chain operationsval user = User().also {
     = "Bob"         //Configuration object    println("Create a user:${}")  // Print log}
// Continuous operation of the same objectFile("")
    .also { () }    // Create a file    .also { ("content") }  // Write content    .also { println("File path:${}") }  // Print path

Best Practices:

  • Side Effects Treatment: Insert logs, assignments and other non-core logic in chain calls
  • Keep object referenced: Return to the receiver itself, supporting continued call to other functions (such as.also(...).apply(...)

3. Comparison table: Quick selection guide

function Recipient Quotation Return value Core uses null safe Scope Type Typical scenarios
apply this Recipient object Object configuration no Recipient scope Initialize objects and set properties
let it lambda results Empty safe processing, return new value yes(?. Independent scope Handle nullable objects, limit scope
run this lambda results Member operations + function calls no Recipient scope Mixed call object methods and external functions
with Parameters passed in lambda results Non-extended function formrun no Recipient scope Explicitly passed in to the receiver, multi-object operation
also it Recipient object Chain side effects (logs, assignments) no Independent scope Keep object chained calls and perform additional operations

4. Best Practices and Pit Avoidance Guide

1. The preferred object configuration apply

When an object needs to be initialized or attributes are set,applyIt can avoid temporary variables and make the code smoother:

// Recommended: Return directly to the configured objectval button = Button().apply {
    text = "submit"
    setOnClickListener { ... }
}

2. null security first choice let

When processing objects that can be null,letCooperate?.It is the best choice:

// Avoid NPE: Safe call + letnetworkResponse?.let { handle(it) }  

3. Members’ preferred choice run/with

When frequent calls to the receiver member (e.g.())hour,runorwithMore concise:

// Simplify member access {
    if (exists()) readText() else ""
}

4. The preferred chain side effects are also

When performing non-core operations such as logging and variable assignment,alsoCan maintain object chain calls:

// Insert logs in chained processdownloadFile()
    .also { logDownload(it) }
    .also { saveToCache(it) }

5. Avoid confusion and return value

  • apply/alsoReturn to the recipient object, suitable for continuing configuration (such as.apply(...).also(...)
  • let/runReturns the lambda result, suitable for generating new values ​​(such asval result = { ... }

5. Summary: The Art of Choice

Kotlin's scoped functions are a perfect combination of functional programming and object-oriented. The key to mastering them is:

  • Clarify the goal: Used to configure objectsapply, use nulllet, for mixed logicrun
  • Follow Return Value: Object chain call selection is requiredapply/also, the calculation result needs to be selectedlet/run
  • Code Style: Used to use extended functionsapply/let/run, accustomed to parameterized callswith

These functions are not mutually exclusive, but complementary. For example,applyCooperatealsoIt can realize the compound operation of "configuration + log",letCooperaterunCan handle null values ​​and execute complex logic. Proficient in using this set of tools can make the code both simplicity and readability, truly reflecting Kotlin's elegance and efficiency.

This is the end of this article about Kotlin scope functions: apply, let, run, with, also. For more related Kotlin apply let run with also, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!