Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

Kotlin lazy delegation is a property delegation mechanism that defers the initialization of a read-only (val) property or local variable until the exact moment of its first access. Upon the initial invocation, the provided lambda expression executes, computes the result, and caches it within the internal state of a Lazy instance. All subsequent accesses bypass the lambda and directly return the cached value.

Syntax and Mechanics

The mechanism is implemented using the by keyword combined with the lazy standard library function.
val propertyName: String by lazy {
    "Computed Value"
}
Since Kotlin 1.1, lazy delegation is not restricted to class properties; it can also be used for local delegated properties inside functions. This defers the initialization of a local variable until it is evaluated in the execution flow.
fun processData() {
    val localVariable: Int by lazy {
        42
    }
    // Initialization lambda executes here
    println(localVariable) 
}
Under the hood, the lazy function takes a lambda and returns an instance of the Lazy<T> interface. The by keyword delegates the getter to the getValue() operator. This operator is an extension function provided statically by the Kotlin standard library for the Lazy<T> interface, rather than being a member function of the instance itself. Because the standard Lazy<T> interface does not provide a setValue() operator, lazy delegation is strictly restricted to read-only declarations using val.

Internal State

A Lazy instance maintains an internal state to track initialization. Before access, the internal value holds a special UNINITIALIZED_VALUE token. Once the lambda completes, this token is overwritten with the computed result, and the reference to the initialization lambda is typically nullified to allow garbage collection.

Thread Safety Modes

The lazy function accepts an optional LazyThreadSafetyMode enum parameter to dictate how the initialization lambda behaves in a multithreaded environment.

1. SYNCHRONIZED (Default)

The initialization is protected by a lock. Only a single thread can execute the lambda and initialize the value. All other threads attempting to access the property will block until the initialization is complete, ensuring a single source of truth.
val safeProperty: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
    "Thread-safe initialization"
}

2. PUBLICATION

Multiple threads can execute the initialization lambda concurrently if the value is uninitialized. However, the internal state uses an atomic compare-and-set (CAS) operation. The first thread to successfully execute the CAS operation on the internal state will have its result cached within the Lazy instance. The results from other concurrent threads are discarded.
val pubProperty: String by lazy(LazyThreadSafetyMode.PUBLICATION) {
    "Concurrent execution, single assignment"
}

3. NONE

No synchronization locks or atomic operations are used. This mode incurs the lowest performance overhead but is strictly unsafe in multithreaded contexts. If multiple threads access the uninitialized property simultaneously, the lambda may execute multiple times, and the final cached value is undefined.
val fastProperty: String by lazy(LazyThreadSafetyMode.NONE) {
    "No synchronization overhead"
}

Custom Locks

When using the default SYNCHRONIZED mode, the Lazy instance uses itself as the synchronization monitor by default. Kotlin allows passing a custom lock object to the lazy function to synchronize the initialization against an external monitor.
val lock = Any()
val lockedProperty: String by lazy(lock) {
    "Synchronized on external lock object"
}
Master Kotlin with Deep Grasping Methodology!Learn More