Scala DISCUSSION

Scala val, var, lazy val and def: when is each evaluated, and why does my trait val print null?

Started by samuelalemayehu Scala val vs deflazy valinitialization orderimmutable referencetrait members
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Scala val, var, lazy val and def: when is each evaluated, and why does my trait val print null?

samuelalemayehu Scala Forum
#1

I have trait Device { val name: String; val label = "Device: " + name } and class Pump extends Device { val name = "pump-1" }. Printing new Pump().label gives Device: null. Changing label to def or lazy val fixes it, but I do not understand why.

What exactly is the difference between val, var, lazy val and def in terms of when the right-hand side is evaluated?

Community replies 5

Re: Scala val, var, lazy val and def: when is each evaluated, and why does my trait val print null?

#2

The four differ in when and how often the right-hand side runs. A val is evaluated once, at the point where it is defined, and cannot be reassigned. A var is also evaluated at its definition but can be assigned again later. A def is evaluated every time it is called. A lazy val is evaluated once, on first access, and the result is cached.

A quick demonstration: with val a = System.nanoTime() and def b = System.nanoTime(), reading a twice gives the same number, and reading b twice gives two different numbers.

Re: Scala val, var, lazy val and def: when is each evaluated, and why does my trait val print null?

#3

Your null comes from initialisation order. When new Pump() runs, the body of the trait Device is initialised before the body of Pump. At the moment label is computed, the field behind name in Pump has not been assigned yet, so it still holds the JVM default, which is null for references (and 0 for numbers). The string is built once from that and never updated.

With def label the expression runs when you call it, after construction has finished. With lazy val label it runs on first access, which is normally also after construction. Both therefore see pump-1.

Re: Scala val, var, lazy val and def: when is each evaluated, and why does my trait val print null?

#4

A guideline that avoids the problem: in traits, declare abstract members as def, and do not compute strict vals from abstract members. A def in a trait can be implemented by a def, a val or a lazy val in the subclass, while an abstract val cannot be implemented by a def, so declaring def leaves implementers the most freedom.

Another fix is to pass the value in, so it is available before any body runs: abstract class Device(val name: String) with class Pump extends Device("pump-1"). Scala 3 allows the same with trait parameters.

Re: Scala val, var, lazy val and def: when is each evaluated, and why does my trait val print null?

#5

Separately from evaluation, remember that val fixes the reference, not the object it points to. val buf = scala.collection.mutable.ArrayBuffer(1, 2) followed by buf += 3 is legal; buf still refers to the same buffer, which now has three elements. Conversely var xs = List(1, 2) followed by xs = xs :+ 3 mutates nothing: it builds a new list and points the variable at it.

The usual preference is val with immutable collections; reach for a var or a mutable collection only inside a small local scope where it clearly pays off, and avoid combining both.

Re: Scala val, var, lazy val and def: when is each evaluated, and why does my trait val print null?

#6

lazy val is not free. Initialisation is synchronised so that it happens once even with several threads, and every access checks whether the value is already there, so it costs a little more than a plain val in hot code. If the initialiser throws, nothing is cached and the next access tries again. Two lazy vals that refer to each other will recurse endlessly or, across threads, can deadlock.

Use it for expensive values that may never be needed, such as a parsed configuration file or a connection, not as a blanket cure for ordering problems.

TEP COMMUNITY