why using plain val in non-final classes

2019-02-19 08:20发布

问题:

If class is not a final one, it may be extended.

There are two possibilities for values: it may be overridden and should be lazy for it, it may not be overridden and should be final.

If val is final - you may assume that all computations over it would work through class hierarchy. If val may be overriden you should declare it lazy for not becoming broken after extending. You may leave val plain and this gives no guaranties it would be extended in right way.

What use cases imply using plain values?


Example of class initialization failure without lazy values

abstract class A {
  lazy val x1 : String = throw new Exception()
  val x2 : String = "mom"
  val x3 : String = x1 + ", " + x2
  println("A: " + x3)
}
class B extends A {
  override lazy val x1: String = "hello"
  println("B: " + x3)
}
class C extends B {
  override val x2: String = "dad"
  println("C: " + x3)
}

testing it:

scala> new B
A: hello, mom
B: hello, mom
res8: B = B@7e2bd615

it works, but further sub-classing broke already existing functionality

scala> new C
A: hello, null
B: hello, null
C: hello, null
res5: C = C@52a53948

setting lazy on x2 fixes the case:

abstract class A {
  lazy val x1 : String = throw new Exception()
  lazy val x2 : String = "mom"
  val x3 : String = x1 + ", " + x2
  println("A: " + x3)
}
class B extends A {
  override lazy val x1: String = "hello"
  println("B: " + x3)
}
class C extends B {
  override lazy val x2: String = "dad"
  println("C: " + x3)
}

right initialization order:

scala> new C
A: hello, dad
B: hello, dad
C: hello, dad
res6: C = C@5e970110

回答1:

Basically, you are screwing it up.

The problem is not the override, the problem is that you are not paying attention to the order in which things are initialized. There are ways to make it work, and ways to make it not work, and you picked the latter. This works:

scala> class C extends { override val x2: String = "dad" } with B {
     |   println("C: " + x3)
     | }
defined class C

scala> new C
A: hello, dad
B: hello, dad
C: hello, dad
res0: C = C@356e3aaf