编译器抱怨延迟DelayedInit的类没有定义delayedInit方法

时间:2012-03-07 16:04:00

标签: scala

我有一个课程,我正在努力扩展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方法运行。但是我用谷歌搜索了,似乎无法找到一个使用示例。我错过了什么?

2 个答案:

答案 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)

@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仍然存在,但默认为,好吧,默认值。这是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