我有以下代码(简化):
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对象无法正常工作这一事实有关?
感谢您的澄清。
答案 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";
}
}