Правила инициализации значений сопутствующих объектов в Scala - PullRequest
2 голосов
/ 25 февраля 2012

В моей игре есть объект Sounds, подобный этому:

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... в конструкторе).

Теперь, если я изменю метод load на следующий:

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

Все звуки загружаются правильно.

Итак, почему это происходит? Как и почему Scala инициализирует объект Sounds, чтобы я мог вызвать load (), но не загружает свои собственные значения в части конструктора? Каковы правила инициализации сопутствующего объекта?

1 Ответ

5 голосов
/ 25 февраля 2012

В соответствии с разделом 5.4 спецификации Scala :

Обратите внимание, что значение, определенное в определении объекта, создается лениво.Новый конструктор 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

Таким образом, ваше неожиданное поведение, вероятно, связано с разделами, которые вы не опубликовали.

...