Maciej Biłas
Maciej Biłas

Reputation: 1976

Avoid an overridden `val` being initialised in the base trait?

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 vals and avoid that happening or should I just stick with lazy vals?

Upvotes: 3

Views: 433

Answers (2)

riccardo.cardin
riccardo.cardin

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

Roman
Roman

Reputation: 5699

Either use lazy val as you mentioned or def. AFAIK there is no other way to avoid the initialization of vals in base classes. This is because everything outside class member definitions goes into the constructor. Therefore vals 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

Related Questions