Компилятор жалуется на класс, который расширяет DelayedInit, не определяя метод delayedInit - PullRequest
2 голосов
/ 07 марта 2012

У меня есть класс, который я пытаюсь сделать расширением DelayedInit:

class Foo extends DelayedInit {
  // expensive initialisation code
}

Однако, когда я пытаюсь запустить sbt compile, я получаю ошибку:

Foo needs to be abstract, since method delayedInit in trait DelayedInit of type (x: => Unit)Unit is not defined

Насколько я понимаю, при расширении черты DelayedInit любой код инициализации должен автоматически заключаться в замыкание и запускаться в методе delayedInit после завершения инициализации.Однако у меня был удар в поиске в Google, и я не могу найти пример использования.Чего мне не хватает?

Ответы [ 2 ]

6 голосов
/ 08 марта 2012

Как работает DelayedInit и его пример использования

DelayedInit черта дает возможность контролировать, в какой момент запускается код инициализации внутри класса или объекта (но не черты).

Любой код инициализации в классах или объектах (но не в признаках), которые унаследованы от DelayedInit, передается компилятором во время инициализации в метод delayedInit, а затем вам решать, когда вы хотите его запустить.

delayedInit метод вызывается автоматически как часть инициализации, и выполнение кода, переданного в качестве параметра сразу в метод, все еще выполняет код во время инициализации.

Начнем с базового сценария:

object Main extends DelayedInit  {

   println ("  initialisation of Main object")
   override def delayedInit (body: => Unit) {
     println("delayedInit")
     body
   }

   def main (args: Array[String]) {
     println("main method") 
   }
}

Напечатает:

delayedInit
  initialisation of Main object
main method

Фактически будет вызван метод delayedInitодин раз для каждого класса, который наследует черту в иерархии классов.Немного более сложный сценарий:

abstract class MyApplication extends DelayedInit {
   println ("  initialisation of MyApplication class")
}

object Main extends MyApplication  {

   println ("  initialisation of Main object")
   override def delayedInit (body: => Unit) {
     println("delayedInit")
     body
   }

   def main (args: Array[String]) {
     println("main method") 
   }
}

Напечатает:

delayedInit
  initialisation of MyApplication class
delayedInit
  initialisation of Main object
main method

Поскольку метод main является первым методом, запускаемым после завершения инициализации,что мы действительно хотим сделать, это сохранить весь код инициализации, переданный в delayedInit, и запустить его позже, вероятно, из main, поскольку потенциально может быть более одного бита кода, который мы могли бы удобно сохранить в ListBuffer(нам нужно продолжать добавлять, чтобы сохранить естественный порядок выполнения).Код внутри объекта 'Main` может выглядеть примерно так:

private val init = new scala.collection.mutable.ListBuffer[()=>Unit]
override def delayedInit (body: => Unit) {
  println("delayedInit")
  init += (()=>body) // will result in NullPointerException
}


def main (args: Array[String]) {
  println("main method") 
  for (code <- init) code ()
}

Однако есть ловушка 22: поскольку инициализация поля init задерживается вместе с каждым другим оператором инициализации, которого нетListBuffer[()=>Unit] объект для сохранения кода инициализации для последующего использования!

Но, помните?

Любой код инициализации внутри классов или объектов (но не черты) , унаследованные от DelayedInit, передаются компилятором во время инициализации в метод delayedInit ...

Давайте немного переставим вещи, перенесем функциональность, которая запоминает код для последующего использования, в черту StoredInit, которая наследуется непосредственно от DelayedInit:

trait StoredInit extends DelayedInit {
   println ("initialisation of StoredInit trait")
   private val init = new scala.collection.mutable.ListBuffer[()=>Unit]
   override def delayedInit (body: => Unit) {
     println("delayedInit")
     init += (()=>body)
   }   
   def initialise () {
     for (code <- init) code ()     
   }
}

// extend StoredInit instead of DelayedInit
abstract class MyApplication extends StoredInit {
   println ("  initialisation of MyApplication class")
}

object Main extends MyApplication  {

   println ("  initialisation of Main object")

   def main (args: Array[String]) {
     println("main method") 
     initialise() // finally perform the delayed initialisation
   }
}

Будет напечатано:

initialisation of StoredInit trait
delayedInit
delayedInit
main method
  initialisation of MyApplication class
  initialisation of Main object

Наконец, почему черта DelayedInit не включает реализацию по умолчанию метода delayedInit?

Сохранитьхук компилятора отделен от фактической реализации отложенной инициализации.Желаемое поведение будет отличаться для консольного приложения и серверного компонента, который должен работать в определенном контейнере.

trait App, однако, наследуется от DelayedInit и обеспечивает реализацию по умолчанию для приложений Scala.

0 голосов
/ 07 марта 2012

@ virtualeyes, в комментарии, был почти прав. Вам нужно переопределить delayedInit, но вы не вставили в него код инициализации.

delayedInit передается код инициализации из основного конструктора как замыкание, поэтому все, что ему нужно сделать, это вызвать это замыкание (если вы хотите, чтобы инициализация произошла сразу после создания класса).

Насколько я вижу, он существует, так что такие черты, как App, могут получить код инициализации и сохранить его для выполнения в другое время. В случае App, это когда вызывается метод main.

Почему DelayedInit не предоставляет реализацию по умолчанию, которая просто запускает замыкание, за мной, но, возможно, кто-то может объяснить?

Во всяком случае, чтобы проиллюстрировать, что я имею в виду, вот сессия REPL:

scala> class A extends DelayedInit {
     | val x = 5
     | println("cats"*x)
     | def delayedInit(x: => Unit) = x
     | }
defined class A

scala> val a = new A
catscatscatscatscats
a: A = A@6192094b

scala> class B extends DelayedInit {
     | val x = 5
     | println("cats"*x) //should still be printed if this is constructor code
     | def delayedInit(x: => Unit) = println("dogs"*5)
     | }
defined class B

scala> val b = new B
dogsdogsdogsdogsdogs
b: B = B@7bff6a5f

И что интересно, я только что заметил, что если вы не вызываете замыкание в delayedInit, то val s, которые вы определили в коде инициализации, все еще существуют, но по умолчанию, ну, по умолчанию, значения по умолчанию , Это переназначение val s ??!

scala> b.x
res5: Int = 0

scala> class C extends DelayedInit {
     | val x = "hello"
     | println(x)
     | def delayedInit(x: => Unit) = println("dogs"*5)
     | }
defined class C

scala> val c = new C
dogsdogsdogsdogsdogs
c: C = C@35f4a968

scala> c.x
res6: java.lang.String = null
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...