Генератор скашек производит нулевой образец
Что случилось? почему человек нуль?
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
Сбой с NullPointerException
Тем не менее, пример работает, если я переместить строку val john = Person("john")
как раз перед property("gen1") = ...
,
Зачем??
Обновить
пример работает, если я объявляю val john
как lazy
, так что, похоже, что lazy val arbPerson
выполняется до val john
, но если это так, компилятор Scala должен потерпеть неудачу, говоря, что john
не определено. john
это val
ни var
так что либо он объявлен и создан, либо нет.
есть идеи по этому поводу?
ps: scala 2.10.3
1 ответ
Итак, кажется, что отложенный val arbPerson выполняется перед val john, но если это так, компилятор scala должен потерпеть неудачу, говоря, что john не определен. john - это val, а не var, поэтому либо он объявлен и создан, либо нет.
Нет, это не правильно. arbPerson
является внутренним методом, который выполняет двойную проверку блокировки и лениво инициализирует приватное поле. Поскольку это метод, его можно вызывать из любого места, включая конструкторы. То, что у вас здесь есть, - это, по сути, следующий Java-код (конечно, он недействителен, как написано здесь, но дает основную идею):
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;
}
}
Ничто в JVM не мешает вам вызывать метод из конструктора, и вот что происходит здесь. arbPerson()
метод вызывается раньше john
поле инициализируется (и по умолчанию все ссылочные поля являются пустыми), поэтому null
поставляется в value()
метод. Отсюда твой NullPointerException
,
Пожалуйста, сообщите, есть ли какие-либо ошибки в моем расширении кода Scala (или редактируйте сообщение напрямую), я их исправлю.