- 谁调用了delayedInit?是在调用main方法之前调用吗?(我猜是)
delayedInit 将由 Scala 编译器自动调用,作为扩展 DelayedInit 特征的对象/类的初始化代码。我在下面详细介绍了这个答案。
- 为什么 initCodes 不是 ListBuffer 元素?我觉得申请只有一个切入点,所以我觉得不应该是复数。
因为可以有一个类的层次结构,其中层次结构中每个类的初始化代码作为执行程序的一部分被执行。下面还提供了一个示例。
- 在哪里可以查看这些知识?我尝试在文档中搜索,但无法搜索。
我通过阅读 Scala 文档及其指向的链接来了解动态。例如这个https://github.com/scala/scala/releases/tag/v2.11.0 和https://issues.scala-lang.org/browse/SI-4330?jql=labels%20%3D%20delayedinit%20AND%20resolution%20%3D%20unresolved
我现在将尝试通过更详细地了解DelayedInit 的工作原理以及 JVM 如何指定程序的入口点来详细说明上述答案。
首先我们要明白,当Scala运行在JVM上时,它还是要遵守JVM定义你程序的入口点的要求,即为JVM提供一个带有main的类签名为public static void main(String[]) 的方法。即使当我们使用App trait 时,看起来我们似乎已经摆脱了这样做,但这只是一种错觉,JVM 仍然需要访问带有签名public static void main(String[]) 的方法。只是通过扩展App和DelayedInit的机制,Scala可以为我们提供这个方法。
其次,重申一下,在类(或对象)定义的主体中找到的代码 sn-ps 将是此类/对象的初始化代码,并且在实例化时会自动执行。在 Java 中,它或多或少是您放在构造函数块中的代码。
所以对于一个类:
class Foo {
// code.
def method = ???
}
无论code是什么,当你调用new Foo时它会自动执行。
如果是对象
object Foo {
// code.
def method = ???
}
code 将自动执行,您无需调用 new,因为 Scala 会自动为您提供一个名为 Foo 的单例实例。
所以基本上,如果正文定义中有任何内容,它会自动执行。您不需要显式执行它。
现在到 DelayedInit 特征。需要注意的一件事是,它为我们提供了一种机制来执行所谓的编译器技巧,其中我们的代码的某些部分被重写。这就是为什么它可能会令人困惑的原因之一。因为当你使用它时,Scala 编译器实际执行的不是你阅读的代码,而是对它的轻微修改。要了解发生了什么,您需要了解编译器更改代码的方式。
DelayedInit trait 允许我们执行的技巧是获取作为类/对象定义主体的一部分的代码,并将其转换为按名称传递的参数,传递给方法 @987654340 @ 在DelayedInit 上定义。
基本上它重写了这个:
object Foo {
// some code
}
进入
object Foo {
// delayedInt({some code})
}
这意味着delayedInt 不是自动执行delayedInt,而是自动调用的方法,// some code 作为参数传递给它。
因此,任何扩展 DelayedInit 的东西都会将其初始化代码替换为方法调用 delayedInt,并将初始化代码作为参数传递。因此,为什么没有人需要显式调用 delayedInt 方法。
现在让我们看看它是如何与 App 特征相关联的,以及 App 特征如何用于提供 Scala 应用程序的入口点。
您会注意到,DelayedInit 特征上的 delayedInit 方法不提供任何实现。因此,delayedInit 在被调用时的实际行为需要由扩展DelayedInit 的其他东西提供。
App trait 就是这样一个实现。 App 特征有什么作用?与讨论主题相关的两个重要事项:
- 它提供了
delayedInit 的实现,它接受它传递的初始化代码,并将其放入ListBuffer。
- 它提供了main方法
def main(args: Array[String]),满足JVM的要求,有一个带有public static void main(String[])的方法作为程序的入口点。而这个 main 方法的作用是执行放在 ListBuffer 中的任何代码。
App trait 的上述特征意味着任何扩展它的对象/类都会将其初始化代码传递给delayedInit,然后将其添加到 ListBuffer 中,然后扩展它的对象/类将现在有一个 main 方法,当调用它时(大部分时间由 JVM 作为入口点)将运行 ListBuffer 中的代码并执行它。
基本上是这样的:
object Foo {
// some code
}
进入这个
object Foo {
// the implementation of delayedInt is to put `// some code` into a list buffer
delayedInt (// some code)
def main(args: Array[String]) = {
// the implementation below just runs through and execute the code found in list buffer that would have been populated by the call to delayedInt and
???
}
}
那么为什么要有一个 List 缓冲区来存储要执行的代码呢?因为,正如我上面所说,可以有一个层次结构的类,层次结构中每个类的初始化代码作为执行程序的一部分被执行。看到这个在行动。
给定以下代码sn-p:
class AnotherClass {
println("Initialising AnotherClass")
}
trait AnotherTrait {
println("Initialising AnotherTrait")
}
trait YetAnotherTrait {
println("Initialising YetAnotherTrait")
}
object Runner extends AnotherClass with AnotherTrait with YetAnotherTrait with App {
println("Hello world")
}
运行时会输出以下内容:
Initialising AnotherClass
Initialising AnotherTrait
Initialising YetAnotherTrait
Hello world
因此,由AnotherClass、AnotherTrait 和YetAnotherTrait 组成的层次结构中的单独初始化代码通过App 特征的delayedInit 方法添加到initCode 列表缓冲区,然后它们由App trait 提供的 main 方法执行。
正如您通过查看源代码会注意到的那样,DelayedInt 的整个机制已被弃用,并计划在未来移除。