Kotlin `lazy`

Using lazy in Kotlin

In Kotlin, the lazy function is used to implement lazy initialization. This means that the value is computed only when it is accessed for the first time. This is particularly useful when the initialization of a variable is resource-intensive, and you want to delay its computation until it's actually needed.

Syntax

The basic syntax for lazy is:

val property: Type by lazy {
    // initialization code
}

Example

Let's consider a scenario where we have a class that computes a complex value. We'll use lazy to initialize this value only when it's first accessed.

Code

package gt.sandbox

import gt.sandbox.util.output.Out

val out = Out.standard()

class ComplexCalculation {
    // A lazily initialized property
    val expensiveValue: Int by lazy {
        out.println("Computing the value...")
        performComplexCalculation()
    }

    private fun performComplexCalculation(): Int {
        // Simulate a complex calculation
        Thread.sleep(1000) // simulate a delay
        return 42 // return some computed value
    }
}

fun main() {
    val calculation = ComplexCalculation()

    // The expensiveValue is not computed until it's accessed for the first time
    out.println("Before accessing expensiveValue")
    out.println("The value is: ${calculation.expensiveValue}") // This triggers the calculation
    out.println("After accessing expensiveValue")
    out.println("Access it again does not trigger re-calculation: ${calculation.expensiveValue}") // This does not trigger the calculation
}

Command to reproduce:

gt.sandbox.checkout.commit 50c8258 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run"

Recorded output of command:

[2024-06-10T02:10:18.653542Z][ms-elapsed-since-start:   21][tname:main/tid:1] Before accessing expensiveValue
[2024-06-10T02:10:18.674603Z][ms-elapsed-since-start:   31][tname:main/tid:1] Computing the value...
[2024-06-10T02:10:19.684012Z][ms-elapsed-since-start: 1041][tname:main/tid:1] The value is: 42
[2024-06-10T02:10:19.684697Z][ms-elapsed-since-start: 1041][tname:main/tid:1] After accessing expensiveValue
[2024-06-10T02:10:19.685008Z][ms-elapsed-since-start: 1042][tname:main/tid:1] Access it again does not trigger re-calculation: 42

Explanation

  1. Declaration: The property expensiveValue is declared with val and initialized using by lazy.
  2. Lazy Initialization Block: The block inside lazy contains the initialization code, which in this case, calls performComplexCalculation().
  3. First Access: When expensiveValue is accessed for the first time, the initialization code runs, and the value is computed. Any subsequent accesses to expensiveValue will return the already computed value, without re-running the initialization block.

Benefits of lazy

  • Performance Optimization: Avoids unnecessary computations until the value is actually needed.
  • Thread Safety: The default mode of lazy is SYNCHRONIZED, which ensures that the initialization block is executed only once in a thread-safe manner. Other modes (PUBLICATION and NONE) can be specified if different behavior is needed.

By using lazy, you can ensure that resources are allocated only when necessary, making your application more efficient.