1
votes

I would be grateful if someone could explain to me why the following causes problems. I've created a small example to demonstrate the issue:

I have a project I'm building with sbt, build.sbt being as follows:

lazy val root = (project in file(".")).
    settings(
        name := "Test",
        version := "1.0.0",
        scalaVersion := "2.11.5",
        libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.4" % Test
    )

and Test.scala in src/main/scala:

object Test extends App
{
    private val map = Map(1 -> 2)
    def access = map(1)

    println(access)
}

and TestSpec.scala (using scalatest) in src/test/scala:

import org.scalatest.FlatSpec

class TestSpec extends FlatSpec
{
    "A Test" should "allow access to the map" in
    {
        assert(Test.access == 2)
    }
}

If I sbt run then '2' gets printed out as expected. If I sbt test then I get a NullPointerException and the test fails:

[info] TestSpec:
[info] A Test
[info] - should allow access to the map *** FAILED ***
[info]   java.lang.NullPointerException:
[info]   at Test$.access(Test.scala:4)
[info]   at TestSpec$$anonfun$1.apply$mcV$sp(TestSpec.scala:7)
etc.

This is because map is null when TestSpec accesses it. I can fix this by changing the map to be a lazy val or a def, but I'd like to know a bit more detail about what's going on with the initialisation order here and how I can avoid this kind of thing in the future. I don't have any problems if I try to access it with another source file in src\main\scala, and I'd like to avoid changing the definitions of my variables in this way to fix this kind of issue.

Thanks

1

1 Answers

3
votes

I didn't know the answer, but it was interesting to research it.

Apparently the trait App is a bit special

http://www.scala-lang.org/api/current/index.html#scala.App

It should be noted that this trait is implemented using the DelayedInit functionality, which means that fields of the object will not have been initialized before the main method has been executed.