Scala - Lazy Val



This chapter takes you through the concept of lazy val in Scala programming. You can use the lazy val to delay the evaluation of a variable until it is accessed for the first time. So, it optimizes performance and resource usage.

The lazy val

The lazy val is a keyword that defines a lazily evaluated variable. Unlike regular val declarations. The lazy val is not initialized until it is referenced. It is used in expensive computations and when the value may not be needed immediately.

The lazy val is declared with the "lazy" keyword followed by the "val" keyword. So, this value is evaluated only once and the result is cached for subsequent accesses.

Syntax

The syntax of a lazy val in Scala is -

lazy val variableName: DataType = expression

Example of Lazy Val

The following example shows defining and using a lazy val in Scala programming

object Demo {
   lazy val expensiveComputation: Int = {
      println("Performing expensive computation...")
      42
   }

   def main(args: Array[String]): Unit = {
      println("Before accessing lazy val")
      println(expensiveComputation)  
      println(expensiveComputation)  
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Before accessing lazy val
Performing expensive computation...
42
42

In the example, the expensiveComputation is not evaluated until it is accessed for the first time. Subsequent accesses do not trigger the computation again because the result is cached.

Advantages of lazy val

There are various advantages of using lazy val. It can optimize performance and save resources. It also improves application responsiveness.

Lazy values avoid unnecessary calculations where the value may never be used during the execution of the program. The lazy val can also resolve circular dependencies in some cases. It delays the evaluation until all dependencies are properly initialized.

lazy val with Collections

You can use lazy val with collections for improving performance when dealing with large datasets and tough operations.

Syntax

The syntax for using lazy val with collections is -

lazy val collectionName: CollectionType = collectionExpression

Example

Consider the example of using lazy val with collections in Scala programming -

object Demo {
   lazy val largeList: List[Int] = {
      println("Initializing large list...")
      List.range(1, 1000000)
   }

   def main(args: Array[String]): Unit = {
      println("Before accessing lazy val")
      println(largeList.head)  
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Before accessing lazy val
Initializing large list...
1

In the example, the largeList is not initialized until it is accessed for the first time. So it delays the expensive operation of creating a large list until it is actually needed.

Combining lazy val with def

While lazy val delays initialization until first access. You can define def methods that are evaluated every time they are called. You can combine these for flexibility in initialization.

Syntax

The syntax for combining lazy val with def is -

def methodName: ReturnType = lazyValueName
lazy val lazyValueName: Type = expression

Example

Consider the example of combining lazy val with def in Scala programming -

object Demo {
   lazy val currentTime: Long = {
      println("Fetching current time...")
      System.currentTimeMillis()
   }

   def getCurrentTime: Long = currentTime

   def main(args: Array[String]): Unit = {
      println("Before accessing lazy val")
      println(getCurrentTime) 
      println(getCurrentTime) 
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Before accessing lazy val
Fetching current time...
<timestamp>
<timestamp>

In the example, currentTime is not evaluated until getCurrentTime is called for the first time. Subsequent calls to getCurrentTime return the cached value.

lazy val with Singleton Objects

You can use lazy val in singleton objects. It is used for initializing resources and configurations that should only be loaded once.

Syntax

The syntax for using lazy val with singleton objects is -

object SingletonObject {
   lazy val valueName: Type = expression
}

Example

Consider the example of using lazy val in a singleton object in Scala programming -

object Configuration {
   lazy val config: Map[String, String] = {
      println("Loading configuration...")
      Map("host" -> "localhost", "port" -> "8080")
   }
}

object Demo {
   def main(args: Array[String]): Unit = {
      println("Before accessing lazy val")
      println(Configuration.config)  
      println(Configuration.config)  
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Before accessing lazy val
Loading configuration...
Map(host -> localhost, port -> 8080)
Map(host -> localhost, port -> 8080)

In the example, the config is not loaded until it is accessed for the first time. So that the configuration is only loaded once.

lazy val and Thread Safety

The lazy val is thread-safe in multi-threaded environment. It guarantees that the value is initialized only once even if accessed concurrently.

Example

Consider the example of using lazy val in a multi-threaded environment in Scala programming -

object Demo {
   lazy val safeValue: Int = {
      println("Initializing safe value...")
      42
   }

   def main(args: Array[String]): Unit = {
      val threads = (1 to 10).map(_ => new Thread(() => println(safeValue)))
      threads.foreach(_.start())
      threads.foreach(_.join())
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Initializing safe value...
42
42
42
42
42
42
42
42
42
42

In the example, safeValue is initialized only once even though multiple threads access it concurrently.

Performance Considerations

While lazy val can improve performance by delaying initialization. But it may introduce overhead due to synchronization.

Example

Consider the example of performance considerations with lazy val in Scala programming -

object Demo {
   lazy val cachedValue: Int = {
      println("Performing expensive computation...")
      Thread.sleep(1000)
      42
   }

   def main(args: Array[String]): Unit = {
      val start = System.currentTimeMillis()
      println(cachedValue)  
      println(cachedValue)  
      val end = System.currentTimeMillis()
      println(s"Time taken: ${end - start} ms")
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

Performing expensive computation...
42
42
Time taken: 1001 ms

In the example, the cachedValue is initialized only once. Hence, the lazy val has the advantage of saving time because it avoids unnecessary calculations.

Lazy Val Summary

  • The lazy val in Scala is a value that is not initialized until it is accessed for the first time.
  • It enhances performance because it defers expensive computations and avoid unnecessary evaluations.
  • The lazy val can be used with collections, singleton objects, and in multi-threaded environments.
  • You can combine lazy val with def. So, it provides flexibility in initialization.
Advertisements