Scala Language Var, Val, and Def Lazy val


Example

lazy val is a language feature where the initialization of a val is delayed until it is accessed for the first time. After that point, it acts just like a regular val.

To use it add the lazy keyword before val. For example, using the REPL:

scala> lazy val foo = {
     |   println("Initializing")
     |   "my foo value"
     | }
foo: String = <lazy>

scala> val bar = {
     |   println("Initializing bar")
     |   "my bar value"
     | }
Initializing bar
bar: String = my bar value

scala> foo
Initializing
res3: String = my foo value

scala> bar
res4: String = my bar value

scala> foo
res5: String = my foo value

This example demonstrates the execution order. When the lazy val is declared, all that is saved to the foo value is a lazy function call that hasn't been evaluated yet. When the regular val is set, we see the println call execute and the value is assigned to bar. When we evalute foo the first time we see println execute - but not when it's evaluated the second time. Similarly, when bar is evaluated we don't see println execute - only when it is declared.

When To Use 'lazy'

  1. Initialization is computationally expensive and usage of val is rare.

    lazy val tiresomeValue = {(1 to 1000000).filter(x => x % 113 == 0).sum}
    if (scala.util.Random.nextInt > 1000) {
      println(tiresomeValue)
    }
    

    tiresomeValue takes a long time to calculate, and it's not always used. Making it a lazy val saves unnecessary computation.

  2. Resolving cyclic dependencies

    Let's look at an example with two objects that need to be declared at the same time during instantiation:

    object comicBook {
      def main(args:Array[String]): Unit = {
        gotham.hero.talk()
        gotham.villain.talk()
      }
    }
    
    class Superhero(val name: String) {
      lazy val toLockUp = gotham.villain
      def talk(): Unit = {
        println(s"I won't let you win ${toLockUp.name}!")
      }
    }
    
    class Supervillain(val name: String) {
      lazy val toKill = gotham.hero
      def talk(): Unit = {
        println(s"Let me loosen up Gotham a little bit ${toKill.name}!")
      }
    }
    
    object gotham {
      val hero: Superhero = new Superhero("Batman")
      val villain: Supervillain = new Supervillain("Joker")
    }
    

    Without the keyword lazy, the respective objects can not be members of an object. Execution of such a program would result in a java.lang.NullPointerException. By using lazy, the reference can be assigned before it is initialized, without fear of having an uninitialized value.