我有一个课程,我正在努力扩展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
方法运行。但是我用谷歌搜索了,似乎无法找到一个使用示例。我错过了什么?
答案 0 :(得分:6)
DelayedInit
的工作原理及其样本用法 DelayedInit
trait提供了控制类或对象(但不是特征)中的初始化代码在何时运行的能力。
继承自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应用程序提供默认实现。
答案 1 :(得分:0)
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
仍然存在,但默认为,好吧,默认值。这是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