# Lazy Initialization

So far, you’ve learned two ways to initialize properties.

  1. Store the initial value at the point of definition, or in the constructor.
  2. Define a custom getter that computes the property for each access.

This atom explores a third use case: costly initialization that you might not need right away, or ever. For example:

  • Complex and time-consuming calculations
  • Network requests
  • Database access

This can produce two problems:

  1. Long application start-up time.
  2. Performing unnecessary work for a property that is never used, or that can have delayed access.

This happens frequently enough that Kotlin includes a built-in solution. A lazy property is initialized when it’s first used, rather than when it’s created. If we never use a lazy property, it never performs that expensive initialization.

The concept of lazy properties isn’t unique to Kotlin. Laziness can be implemented within other languages, whether or not they provide direct support. Kotlin provides a consistent, recognizable idiom for such properties using [property delegation](javascript:void(0)). With a lazy property, by is followed by a call to lazy():

val lazyProperty by lazy { initializer }

lazy() takes a lambda containing the initialization logic. As usual, the last expression in the lambda becomes the result, which is assigned to the property:

// LazyInitialization/LazySyntax.kt
package lazyinitialization
import atomictest.*

val idle: String by lazy {
  trace("Initializing 'idle'")
  "I'm never used"
}

val helpful: String by lazy {
  trace("Initializing 'helpful'")
  "I'm helping!"
}

fun main() {
  trace(helpful)
  trace eq """
    Initializing 'helpful'
    I'm helping!
  """
}

The idle property isn’t initialized because it’s never accessed.

Notice that both helpful and idle are vals. Without lazy initialization, you’d be forced to make them vars, producing less-reliable code.

We can see all the work that lazy initialization does for you by implementing the behavior for an Int property without it:

// LazyInitialization/LazyInt.kt
package lazyinitialization
import atomictest.*

class LazyInt(val init: () -> Int) {
  private var helper: Int? = null
  val value: Int
    get() {
      if (helper == null)
        helper = init()
      return helper!!
    }
}

fun main() {
  val later = LazyInt {
    trace("Initializing 'later'")
    5
  }
  trace("First 'value' access:")
  trace(later.value)
  trace("Second 'value' access:")
  trace(later.value)
  trace eq """
    First 'value' access:
    Initializing 'later'
    5
    Second 'value' access:
    5
  """
}

The value property doesn’t store a value, but instead has a getter that retrieves the value from the helper property. This is similar to the code Kotlin generates for lazy.

Now we can compare the three ways to initialize a property—at the point of definition, using a getter, and using lazy initialization:

// LazyInitialization/PropertyOptions.kt
package lazyinitialization
import atomictest.trace

fun compute(i: Int): Int {
  trace("Compute $i")
  return i
}

object Properties {
  val atDefinition = compute(1)
  val getter
    get() = compute(2)
  val lazyInit by lazy { compute(3) }
  val never by lazy { compute(4) }
}

fun main() {
  listOf(
    Properties::atDefinition,
    Properties::getter,
    Properties::lazyInit
  ).forEach {
    trace("${it.name}:")
    trace("${it.get()}")
    trace("${it.get()}")
  }
  trace eq """
    Compute 1
    atDefinition:
    1
    1
    getter:
    Compute 2
    2
    Compute 2
    2
    lazyInit:
    Compute 3
    3
    3
  """
}
  • atDefinition is initialized when you create an instance of Properties.
  • “Compute 1” appears before “atDefinition:” which shows that initialization happens before any accesses.
  • getter is computed every time you access it. “Compute 2” appears twice, once for each access to the property.
  • The initialization value for lazyInit is only calculated the first time it is accessed. Initialization never happens if you don’t access that property—notice that “Compute 4” never appears in the trace.

Exercises and solutions can be found at www.AtomicKotlin.com.