Reputation: 12720
what is wrong? why person is null?
import org.scalacheck.{Arbitrary, Properties, Gen, Prop}
import Gen._
import Prop._
case class Person(name: String) {}
object QuickCheckPerson extends Properties("Person") {
property("gen1") = forAll { (person: Person) =>
println("person: " + person)
person.name == "john" // nullPointerException, because person == null
}
val john = Person("john")
implicit lazy val arbPerson: Arbitrary[Person] = Arbitrary(value(john))
}
QuickCheckPerson.check
It fails with NullPointerException.
However, the example works if I move the line val john = Person("john")
just before property("gen1") = ...
.
why??
Update
the example works if I declare the val john
as lazy
.
so, it seems that the lazy val arbPerson
is executed before the val john
, but if so,
the scala compilator should fail saying that john
is not defined. john
is a val
, nor a var
, so either it is declared and instantiated, or not.
any idea on this?
ps: scala 2.10.3
Upvotes: 0
Views: 1018
Reputation: 127771
so, it seems that the lazy val arbPerson is executed before the val john, but if so, the scala compilator should fail saying that john is not defined. john is a val, nor a var, so either it is declared and instantiated, or not.
No, this is not correct. arbPerson
is a method internally, which performs double-checked lock and lazily initializes a private field. Since it is a method, it can be called from anywhere, including constructors. What you have here is essentially the following Java code (of course, it is not valid as it is written here, but it gives the basic idea):
public class QuickCheckPerson extends Properties {
private final Person john;
private volatile Arbitrary<Person> arbPerson;
public QuickCheckPerson() {
super("Person"); // call superclass constructor
// property field belongs to superclass
property.update("gen1", forAll(new Function1<Person, Boolean>() {
/* closure implementation */
},
/* implicit parameter converting boolean to Prop */,
arbPerson(),
/* other implicits */
));
this.john = Person.apply("john");
}
public Arbitrary<Person> arbPerson() {
// Perform double-checked lock over a bit field (not presented here)
// and create arbPerson if needed
// Let's pretend that the following is double-checked lock:
if (arbPerson == null) {
arbPerson = Arbitrary.apply(value(john));
}
return arbPerson;
}
}
Nothing in JVM prevents you from calling a method from a constructor, and this is what happens here. arbPerson()
method is called before john
field is initialized (and by default all reference fields are nulls), so null
is supplied to value()
method. Hence your NullPointerException
.
Please do tell if there are any mistakes in my expansion of Scala code (or edit the post directly), I will fix them.
Upvotes: 1