For-expression在Companion对象中声明的集合上抛出NullPointerException

时间:2014-01-12 14:07:05

标签: scala

我有以下代码(简化):

case class Element(x : Int, y : Int)

case class Controller(elems: Set[Element]) {
  import Controller._

  def neighbours(e : Element) : Set[Element] = Set.empty[Element] // this is only to get the code to compile
  def merge(that : Controller) : Controller = Controller(elems ++ that.elems)

  def evolve : Controller = {
    {
      println(s"Evolving using rules $rules");
      for(rule <- rules) yield {
        println(s"Handling rule $rule")
        Controller(for(c <- elems; if !rule(c,neighbours(c))) yield c)
      }
    }.foldLeft(empty)(_.merge(_))
  }
}

object Controller{
  type Rule = (Element, Set[Element]) => Boolean
  val empty = Controller(Set.empty[Element])

  val rules : Set[Rule] = Set(rule1,rule2)
  val rule1 : Rule = (e : Element, set : Set[Element]) => set.size < 2
  val rule2 : Rule = (e : Element, set : Set[Element]) => set.size < 4 && set.size > 1
}

我的问题是evolve方法抛出了NullPointer异常。 经过一些调查后,我意识到问题出在同伴对象中的rules设置,由于某种原因,此时使用的是Set(null),如println(s"Evolving using rules $rules");中的println所示。

任何人都可以解释为什么会这样吗? 为什么规则设置为(null),如果我向它添加两个rule,我在下面的行中匿名实现?
问题是否与我有一个案例类以及Companion对象无法正常工作这一事实有关?

感谢您的澄清。

1 个答案:

答案 0 :(得分:2)

TLDR:这是初始化顺序。

您的代码可以缩短为

object Foo { 
    val foo = Set(bar1, bar2)
    val bar1 = "a"
    val bar2 = "b"
}

问题是声明是以自上而下的方式处理的,所以在foo初始化并设置了值时,bar1和bar2尚未分配,因此调用看起来像

val foo = Set(null, null)

显然导致Set(null)。

要解决此问题,请在规则之前移动rule1和rule2(或者,将val交换为def或将lazy val应用于rule1 / rule2,但我怀疑是否适合您)。

你可能会争辩说,在java中你不会有同样的问题,但你会 - 几乎完全的翻译将是:

class Foo {
    Set<String> foo;
    String bar1;
    String bar2;

    Foo() {
        foo = new HashSet<String>();
        foo.add(bar1);
        foo.add(bar2);
        bar1 = "a";
        bar2 = "b";
    }
}