Генератор скашек производит нулевой образец

Что случилось? почему человек нуль?

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 (или редактируйте сообщение напрямую), я их исправлю.

Другие вопросы по тегам