When to use val or def in Scala traits?

A def can be implemented by either of a def, a val, a lazy val or an object. So it’s the most abstract form of defining a member. Since traits are usually abstract interfaces, saying you want a val is saying how the implementation should do. If you ask for a val, an implementing class cannot use a def.

A val is needed only if you need a stable identifier, e.g. for a path-dependent type. That’s something you usually don’t need.


Compare:

trait Foo { def bar: Int }

object F1 extends Foo { def bar = util.Random.nextInt(33) } // ok

class F2(val bar: Int) extends Foo // ok

object F3 extends Foo {
  lazy val bar = { // ok
    Thread.sleep(5000)  // really heavy number crunching
    42
  }
}

If you had

trait Foo { val bar: Int }

you wouldn’t be able to define F1 or F3.


Ok, and to confuse you and answer @om-nom-nom—using abstract vals can cause initialisation problems:

trait Foo { 
  val bar: Int 
  val schoko = bar + bar
}

object Fail extends Foo {
  val bar = 33
}

Fail.schoko  // zero!!

This is an ugly problem which in my personal opinion should go away in future Scala versions by fixing it in the compiler, but yes, currently this is also a reason why one should not use abstract vals.

Edit (Jan 2016): You are allowed to override an abstract val declaration with a lazy val implementation, so that would also prevent the initialisation failure.

Leave a Comment