Как работает 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.