Willy
Willy

Reputation: 101

Scala NullPointerException during initialization

Consider the following case (this is a simplified version of what I have. The numberOfScenarios is the most important variable here: I usually use a hardcoded number instead of it, but I'm trying to see if it's possible to calculate the value):

object ScenarioHelpers {
    val identifierList = (1 to Scenarios.numberOfScenarios).toArray
    val concurrentIdentifierQueue = new ConcurrentLinkedQueue[Int](identifierList.toSeq)
}

abstract class AbstractScenario {
    val identifier = ScenarioHelpers.concurrentIdentifierQueue.poll()
}

object Test1 extends AbstractScenario {
    val scenario1 = scenario("test scenario 1").exec(/..steps../)
}

object Test2 extends AbstractScenario {
    val scenario2 = scenario("test scenario 2").exec(/..steps../)
}

object Scenarios {
    val scenarios = List(Test1.scenario1, Test2.scenario2)
    val numberOfScenarios = scenarios.length
}

object TestPreparation {
    val feeder = ScenarioHelpers.identifierList.map(n => Map("counter" -> n))
    val prepScenario = scenario("test preparation")
        .feed(feeder)
        .exec(/..steps../)
}

Not sure if it matters, but the simulation starts with executing the TestPreparation.prepScenario.

I see that this code contains a circular dependency which makes this case impossible in and of itself. But I get a NullPointerException on the line in AbstractScenario where identifier is being initialized.

I don't fully understand all this, but I think it has something to do with the vals being simply declared at first and the initialization does not happen until later. So when identifier is being initialized, the concurrentIdentifierQueue is not yet initialized and is therefore null.

I'm just trying to understand the reasons behind the NullPointerException and also if there's any way to get this working with minimal changes? Thank you.

Upvotes: 1

Views: 516

Answers (2)

Piro
Piro

Reputation: 1435

You answered that yourself:

I see that this code contains a circular dependency which makes this case impossible in and of itself. But I get a NullPointerException on the line in AbstractScenario where identifier is being initialized.

  • val feeder = ScenarioHelpers.identifierList... calls ScenarioHelpers initialization
  • val identifierList = (1 to Scenarios.numberOfScenarios).toArray calls Scenarios initialization
  • val scenarios = List(Test1.scenario1, Test2.scenario2) calls Test1 inicialization including AbstractScenario
  • Here val identifier = ScenarioHelpers.concurrentIdentifierQueue.poll() ScenarioHelpers is still initializing and identifierList is null.

You have to get numberOfScenarios in noncyclic way. Personally I would remove identifierList and assign identifier other way - incrementing counter or so.

Upvotes: 1

simpadjo
simpadjo

Reputation: 4017

NPEs during trait initialization is a very common problem. The most robust way to resolve it is avoiding implementation inheritance at all.

if it is not possible for some reasons you can mark problematic fields lazy val or def instead of val.

Upvotes: 2

Related Questions