Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Demo of why vals in traits are "bad" (aka you have to be very careful)
The issue is that the superclass gets instantiated first, and if it has an eager val that
calls an abstract def (or abstract lazy val, if such a thing is possible) it will capture
the not-yet-fully-initialized value of that val in the subclass (which is usually null).
scala> trait T { def f: String; val callsF = f } // *val* `callsF` calls abstract def `f` -- risky!
defined trait T
scala> object X extends T { val f = "foooo" } // `f` is implemented with a strict val -- bomb is set
defined object X
scala> X.callsF
res0: String = null // unexpectedly, X.callsF returns null instead of "foooo"
scala> object Y extends T { def f = "baaar" } // implement `f` with a def -- all good
defined object Y
scala> Y.callsF
res2: String = baaar // as we'd hope
So you either have an out-of-band agreement that all subclasses must implement the
trait with only `def`s, or you only use defs (and lazy vals if you must) in your trait,
and the implementors are free to do whatever. I like the latter option personally.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment