在Scala中初始化伴随对象值的规则

时间:2012-02-25 10:18:29

标签: scala initialization lazy-evaluation lazy-initialization

我的游戏有一个像这样的声音对象:

object Sounds {
  SoundFactory.setAssetBasePath("mfx/")

  val EXPLOSION_0 = ESound("explosion1.ogg")
  val EXPLOSION_1 = ESound("explosion2.ogg")
  val EXPLOSION_2 = ESound("explosion3.ogg")
  val IMPACT_0 = ESound("impact1.ogg", 0.4f)
  val IMPACT_1 = ESound("impact2.ogg", 0.4f)
  val IMPACT_2 = ESound("impact3.ogg", 0.4f)
  val BONUS = ESound("bonus.ogg", 0.7f)

  // -- snip --

  def load() {
    println("Sounds loaded")
  }

  case class ESound(sound_file: String, volume: Float = 1) {
    private val sound = SoundFactory.createSoundFromAsset(AndEngine.engine.getSoundManager, AndEngine.activity.get, sound_file)
    sound.setVolume(volume)
    sound.setLoopCount(0)

    def play() { sound.play() }
  }
}

为简洁起见,我删除了许多方法等。但基本的想法是Scala懒洋洋地将对象初始化,所以我第一次在这个对象上调用一些方法(load())它将被初始化。这可以在例如纹理加载等之后完成。

但是使用上面的代码,我第一次在游戏中按下某个菜单按钮时,我会暂停一下,因为它只会加载所有这些声音(由构造函数中的SoundFactory.createSound...引起)。 / p>

现在,如果我将load方法更改为以下内容:

    println("Sounds loaded, " + BONUS.toString)

所有声音都能正确加载。

那么,为什么会这样呢? Scala如何以及为什么初始化Sounds对象以便我可以调用load()但是不在构造函数部分中加载它自己的值?伴随对象初始化有哪些规则?

1 个答案:

答案 0 :(得分:5)

根据Scala specification的第5.4节:

  

请注意,对象定义定义的值是懒惰实例化的。该   新的m $ cls构造函数不是在对象定义的点上进行评估,而是在   而是在程序执行期间第一次取消引用m时进行评估   (可能根本就没有)。试图在课程中再次取消引用m   构造函数的评估导致无限循环或运行时错误。其他   尝试取消引用的线程在构造函数被评估时阻塞直到   评估完成。

伴随对象应该在第一次被引用时构建 - 我认为这也是你的理解。这适用于以下示例:

object Sounds {
  val EXPLOSION_0 = ESound("EXPLOSION_0")
  def load() { println("loaded") }
  case class ESound(file: String) {
    private val sound = {
      println("waiting 1s before loading " + file)
      Thread.sleep(1000)
      "sound from " + file
    }
  }
}

object C extends App {
  Sounds.load()
}

打印:

[info] Running C
waiting 1s before loading EXPLOSION_0
loaded

因此,您的意外行为可能来自您尚未发布的部分。