In my understanding,
When an object of subclass is created, the constructor of the subclass
first calls the constructor of parent class. In the parent
constructor, if there is a def that has been overridden then the
overridden copy is called.
Due to all this, in the following code the 'env' variable is not set to (intuitively) the correct length of 1.
class C {
val range: Int = 10
val env = new Array[Int](range)
}
class A extends C {
override val range = 1
}
val a = new A
a.env.length //0 instead of 1 or 10
Now, if we define range as a lazy val then the problem goes away and a.env.length yields 1. I am not able to understand why? (because the moment new Array[Int](range) is called, I suppose, it will call the getter for range from subclass and that will return 0.
Can someone clarify, why lazy val solves this issue.
The one-question FAQ moved to:
http://docs.scala-lang.org/tutorials/FAQ/initialization-order.html
lazy val is on-demand initialization, whereas an "eager" val, a misnomer, waits patiently, even with indifference, for the constructor in which it is defined to be run.
Related
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.
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.
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.
Demo scala code:
trait A {
val a = 3
val b = a + 2
}
trait B extends A {
override val a = 10
}
object X extends B
println(X.b)
It prints value: 2, why is it not 5 or 12?
To answer the why:
In Scala when you write
class A {
val a = 2
}
The value is initialized in the constructor of the class (the same behavior applies to traits and objects). Furthermore, superclasses are initialized before subclasses. This leads to the following behavior for your use case:
B is created (memory is reserved), with two variables a and b whose value is 0. Now the constructor of A is invoked. Because a is overwritten in a subclass and due to Scalas dynamic binding nature, it is not assigned with 2, but with the value of the subclass. You want to be it 10, but because this assignment happens in the constructor of B (which is not yet invoked) the default value 0 is assigned. Now, b is assigned. Because it is not overwritten, the value a+2 is chosen, where a is 0. Because the constructor of A is finished here, the constructor of B can be invoked, which assigns 10 to a.
Hence, a is 10 and b is 2.
To answer what to do against this behavior bug:
Don't use vals as long as you don't absolutely understand about the problems that can arise. Use defs or lazy vals instead, there values are not initialized in the constructor of a class and therefore can be easily overwritten. If you absolutely need a val in a trait, then make it final
It is possible to mark a var as initialization independent for the subclass, which can be done with var a: Type = _. This tells the compiler to not initialize this variable in the constructor of the defining class (but means that the value needs to stay mutable). It can then easily be assigned in the subclass. This gets important when the in the constructor of the superclass as method is called, that initializes a var:
class A {
f()
def f() = ()
}
class B extends A {
// don't initialize this var with anything else here or
// the later assignment will be overwritten
var b: Int = _
override def f() =
b = 5
}
new B().b // prints 5
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Scala: forward references - why does this code compile?
object Omg {
class A
class B(val a: A)
private val b = new B(a)
private val a = new A
def main(args: Array[String]) {
println(b.a)
}
}
the following code prints "null". In java. similar construction doesn't compile because of invalid forward reference. The question is - why does it compile well in Scala? Is that by design, described in SLS or simply bug in 2.9.1?
It's not a bug, but a classic error when learning Scala. When the object Omg is initialized, all values are first set to the default value (null in this case) and then the constructor (i.e. the object body) runs.
To make it work, just add the lazy keyword in front of the declaration you are forward referencing (value a in this case):
object Omg {
class A
class B(val a: A)
private val b = new B(a)
private lazy val a = new A
def main(args: Array[String]) {
println(b.a)
}
}
Value a will then be initialized on demand.
This construction is fast (the values are only initialized once for all application runtime) and thread-safe.
The way that I understand it, this has to do with how the Scala classes are created. In Java, the class defined above would be initializing the variables inline, and since a had not been defined yet, it could not be compiled. However, in Scala it is more the equivalent of this in Java (which should also produce null in the same scenario):
class Omg {
private B b = null;
private A a = null;
Omg(){
b = new B(a);
a = new A();
}
}
Alternately, you could make your declaration of b lazy, which would defer setting the value until it is called (at which time a will have been set).
If this is a concern, compile with -Xcheckinit during development and iterate until the exceptions go away.
Spec 5.1 for template body statements executed in order; beginning of 4.0 for forward references in blocks.
Forward References - why does this code compile?
As #paradigmatic states, it's not really a bug. It's the initialization order, which follows the declaration order. It this case, a is null when b is declared/init-ed.
Changing the line private val b = new B(a) to private lazy val b = new B(a) will fix issue, since using lazy will delay the init. of b to it's first usage.
It's very likely that this behavior is described in the SLS.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Scala: forward references - why does this code compile?
object Omg {
class A
class B(val a: A)
private val b = new B(a)
private val a = new A
def main(args: Array[String]) {
println(b.a)
}
}
the following code prints "null". In java. similar construction doesn't compile because of invalid forward reference. The question is - why does it compile well in Scala? Is that by design, described in SLS or simply bug in 2.9.1?
It's not a bug, but a classic error when learning Scala. When the object Omg is initialized, all values are first set to the default value (null in this case) and then the constructor (i.e. the object body) runs.
To make it work, just add the lazy keyword in front of the declaration you are forward referencing (value a in this case):
object Omg {
class A
class B(val a: A)
private val b = new B(a)
private lazy val a = new A
def main(args: Array[String]) {
println(b.a)
}
}
Value a will then be initialized on demand.
This construction is fast (the values are only initialized once for all application runtime) and thread-safe.
The way that I understand it, this has to do with how the Scala classes are created. In Java, the class defined above would be initializing the variables inline, and since a had not been defined yet, it could not be compiled. However, in Scala it is more the equivalent of this in Java (which should also produce null in the same scenario):
class Omg {
private B b = null;
private A a = null;
Omg(){
b = new B(a);
a = new A();
}
}
Alternately, you could make your declaration of b lazy, which would defer setting the value until it is called (at which time a will have been set).
If this is a concern, compile with -Xcheckinit during development and iterate until the exceptions go away.
Spec 5.1 for template body statements executed in order; beginning of 4.0 for forward references in blocks.
Forward References - why does this code compile?
As #paradigmatic states, it's not really a bug. It's the initialization order, which follows the declaration order. It this case, a is null when b is declared/init-ed.
Changing the line private val b = new B(a) to private lazy val b = new B(a) will fix issue, since using lazy will delay the init. of b to it's first usage.
It's very likely that this behavior is described in the SLS.
Why does a null pointer exception occur in the code below?
object Test extends App{
trait MyTrait[A]{ self =>
val seq: Seq[A]
val size = seq.size // null pointer here
}
val p = new MyTrait[Int]{
val seq = Seq(1,2,3)
}
}
If I change the size field to lazy then it's ok.
Fields are initialized in the order they are mixed in--so first everything in the trait happens, and then the val is assigned to Seq(1,2,3) (since you're essentially mixing in an anonymous trait).
As you have discovered, lazy val is often a way out of this mess: you don't actually call seq.size until you need it, which is after you have populated the seq field.
The style recommendation is to avoid vals in traits, in favor of defs and lazy vals, because of init order land mines.
Sample conversation:
https://groups.google.com/forum/?fromgroups=#!topic/scala-user/nrOrjPOYmb0