I was going through the effective scala slides and it mentions on slide 10 to never use val
in a trait
for abstract members and use def
instead. The slide does not mention in detail why using abstract val
in a trait
is an anti-pattern. I would appreciate it if someone can explain best practice around using val vs def in a trait for abstract methods
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 val
s 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 val
s.
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.