Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected Result when Overriding 'val'

In Scala 2.10.4, Given the following class:

scala> class Foo { 
     |   val x = true
     |   val f = if (x) 100 else 200
     | }
defined class Foo

The following two examples make sense to me:

scala> new Foo {}.f
 res0: Int = 100

scala> new Foo { override val x = false}.f
res1: Int = 200

But, why doesn't this call return 100?

scala> new Foo { override val x = true }.f
res2: Int = 200
like image 232
Kevin Meredith Avatar asked Mar 27 '15 17:03

Kevin Meredith


1 Answers

Because vals aren't initialized more than once, x is actually null (or false for a default Boolean) during the initialization of Foo, and then initialized in the anonymous class that is extending Foo in your example.

We can test it more easily with an AnyRef:

class Foo { 
    val x = ""
    val f = if (x == null) "x is null" else "not null"
}

scala> new Foo { override val x = "a" }.f
res10: String = x is null

scala> new Foo {}.f
res11: String = not null

There's a full explanation in the Scala FAQ. Excerpt:

Naturally when a val is overridden, it is not initialized more than once. So though x2 in the above example is seemingly defined at every point, this is not the case: an overridden val will appear to be null during the construction of superclasses, as will an abstract val.

A simple way to avoid this would be to use a lazy val or def, if the val being referenced may be overridden.

Additionally, you can use the -Xcheckinit compiler flag to warn you about potential initialization errors like this.

like image 133
Michael Zajac Avatar answered Nov 04 '22 01:11

Michael Zajac