1

我在 Scala 中编写俄罗斯方块作为练习,我遇到了这种奇怪的行为:

abstract class Orientation(protected val index: Int) {
  private[Orientation] val impls: Array[Orientation] = Array(OrientedLeft, OrientedUp, OrientedRight, OrientedDown)

  def rotateCounterClockwise = impls(
      if (index == 0) 3
      else index - 1
  )

  def rotateClockwise = impls((index + 1) % 4)
}

object OrientedLeft extends Orientation(0) {
  override def toString = "Left"
}
object OrientedUp extends Orientation(1) {
  override def toString = "Up"
}
object OrientedRight extends Orientation(2) {
  override def toString = "Right"
}
object OrientedDown extends Orientation(3) {
  override def toString = "Down"
}

object Test extends Application {
  var orientation: Orientation = OrientedUp

  for (i <- 0 until 20) {
    println("Oriented to: "+ orientation)
    orientation = orientation.rotateClockwise
  }
}

运行测试给出以下输出:

Oriented to: Up
Oriented to: Right
Oriented to: Down
Oriented to: null

后面跟着一个 java.lang.NullPointerException 显然。我的意思是:这到底是怎么回事?

4

2 回答 2

6

只需移动impls一个伴生对象:

object Orientation {
  private val impls: Array[Orientation] = Array(OrientedLeft, OrientedUp, OrientedRight, OrientedDown)
}
abstract class Orientation(protected val index: Int) {
  import Orientation._
  def rotateCounterClockwise = impls(
      if (index == 0) 3
      else index - 1
  )

  def rotateClockwise = impls((index + 1) % 4)
}

出现此错误的原因是您具有循环初始化依赖项:每次实例化Orientation时,都会访问四个Orientation单例。简单地说,访问OrientedUp强制它的实例化,这反过来又强制所有四个单例的实例化,包括OrientedUp它自己,它仍在构造中。这就是为什么你会为这个“仍在构造”的值得到 null 的原因。

于 2013-08-06T13:09:09.537 回答
2

这可能是一个初始化问题。每个实例Orientation都有一个OrientedX-objects 数组,它们本身就是Orientation. Scalaobject被延迟初始化,但这会触发impls使用当前正在初始化的对象填充数组。因此null进入。

按照Régis 的建议或标记val implslazy

于 2013-08-06T13:12:42.187 回答