7

If I write :

trait T {
  val t = 3
  val u = 1::t::Nil
}

class U extends T {
  override val t = 2
}

(new U).u

it shows this.

List(1, 0)

How should I change the above code to make it display the following:

List(1, 2)

i.e. override val t sets the value of t for u in the trait T?

Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
Mikaël Mayer
  • 10,425
  • 6
  • 64
  • 101

3 Answers3

12

One way to do this is to delay evaluation of u by using def or lazy val as follows:

trait T {
  def t = 3
  def u = 1::t::Nil
}

class U extends T {
  override def t = 2
}

(new U).u

or

trait T {
  val t = 3
  lazy val u = 1::t::Nil
}

class U extends T {
  override val t = 2
}

(new U).u 

The differences are as follows:

  • val makes an expression evaluate during initialization
  • def makes an expression evaluate each time u is used
  • lazy val makes it evaluated on first u usage and caches the result
Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
Denis R.
  • 818
  • 1
  • 9
  • 15
10

Try using an early initializer:

scala> trait T {
     |   val t = 3
     |   val u = 1::t::Nil
     | }
defined trait T

scala> class U extends {
     | override val t = 2;
     | } with T
defined class U

scala> (new U).u
res1: List[Int] = List(1, 2)

See e.g. here for more information about early initialization.

Community
  • 1
  • 1
Steve Waldman
  • 13,689
  • 1
  • 35
  • 45
  • Although correct, for readability reasons, I prefer to use the cache method with lazy vals. If I need to add code to my class U, I cannot put it in the brackets, I need to create another group, like class U extends { override val t=2} with T { ... my code here ...} – Mikaël Mayer Apr 03 '13 at 12:28
  • The issue with this approach is that every access is synchronized to make sure that the `lazy val` is in fact ready. It slows down application and should not be used blindly on critical paths. – Jacek Laskowski Dec 27 '14 at 14:47
4

All scala declarative style is just an illusion. Scala is build upon a jvm and works like java.

Evetything is a class and should be independent on its usage (java is not c++ and supports incremental build with its pros and cons). Every trait has its own initialization code and multi-trait class runs respective initialization code one by one. If you use some AnyRef that is declared only in a subclass than that its value will be set for null during initialization.

I guard myself with specifing convention rule: every val should be either final or lazy (why using plain val in non-final classes) . So I don't care about initialization order and may pretend further that I'm using declarative language.

Also I'm using option -Xcheckinit: Add runtime check to field accessors.

Community
  • 1
  • 1
ayvango
  • 5,867
  • 3
  • 34
  • 73