Reputation: 1976
In the following code:
trait Base {
val foo: String = {
println("Hi, I'm initializing foo in trait Base")
"foo"
}
}
class Overrider extends Base {
override val foo = "bar!"
}
object Runner extends App {
println(new Overrider().foo)
println((new {override val foo = "baz"} with Base).foo)
}
Base
trait's foo
value initialisation is called regardless of whether I override the val by extending the trait or using an early initialiser:
Hi, I'm initializing foo in trait Base
bar!
Hi, I'm initializing foo in trait Base
baz
Is there a way to use val
s and avoid that happening or should I just stick with lazy val
s?
Upvotes: 3
Views: 433
Reputation: 8353
As other users answered to your question, you have to define foo
as a def
if you do not want the Base
trait method being valuated.
You told to me in the comments of your question that you were trying to implement a wiring module, as the one described in this link. Then, you're basically trying to implement the thin cake pattern.
In this case, it is not logically correct to declare foo
as a val
. foo
represents a dependency that cannot be eagerly resolved . If you use a val
, the two components will be tight coupled. You've to define foo
as a def
to let to your main application (or test) to wire foo
to the correct type, i.e. a concrete class or a mock.
Let me know if you want some more explanations.
Upvotes: 0
Reputation: 5699
Either use lazy val
as you mentioned or def
. AFAIK there is no other way to avoid the initialization of val
s in base classes. This is because everything outside class member definitions goes into the constructor. Therefore val
s will be initialized on construction time.
Another approach would be to define an interface which you extend from:
trait Base {
def foo: String
}
class Foo extends Base {
override val foo = "foo"
}
class Bar extends Base {
override val foo = "bar"
}
Upvotes: 3