Scala App特性和main如何在内部运作?

时间:2018-11-25 14:12:07

标签: scala

嗨,我是Scala的新手。

据我所知,在Scala中有两种创建入口点的方式,一种是用对象定义主方法,另一种是扩展 App 特性。

我想知道 App 特性的工作原理,所以我检查了 App 特性的来源。充满混乱的代码...

代码表示该应用具有 initCodes ,这些特征是从 App 特征扩展而来的,并添加了继承自 DelayedInit delayedInit 方法中。同样, App 特征具有主要方法,它将作为入口点。

但是让我感到困惑的是

  1. 谁呼叫 delayedInit ?是在调用main方法之前调用它的吗?(我想是的)
  2. 为什么 initCodes ListBuffer 的一个元素?我认为应用程序中只有一个切入点,所以我不认为它应该是复数。
  3. 我在哪里可以检查这些知识?我试图在文档中搜索,但无法

2 个答案:

答案 0 :(得分:6)

  
      
  1. 谁叫delayInit?是在调用main方法之前调用它的吗?(我想是的)
  2.   

Scala编译器将自动调用delayedInit作为扩展DelayedInit特性的对象/类的初始化代码。我在下面进一步扩展了这个答案。

  
      
  1. 为什么initCodes不是ListBuffer的元素?我认为应用程序中只有一个切入点,所以我不认为它应该是复数。
  2.   

因为可能有一个类的层次结构,其中层次结构中每个类的初始化代码都作为执行程序的一部分而执行。下面还提供了一个示例。

  
      
  1. 我在哪里可以检查这些知识?我试图在文档中搜索,但是找不到。
  2.   

我必须通过阅读Scala文档及其指向的链接来了解动态。例如,此https://github.com/scala/scala/releases/tag/v2.11.0https://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特征时,看起来似乎我们正在远离它,但这只是一种幻想,JVM仍然需要访问带有签名{{1}的方法}。仅仅是通过扩展public static void main(String[])App的机制,Scala可以代表我们提供此方法。

第二,重申在类(或对象)定义的正文中找到的代码片段也将是此类/对象的初始化代码,并在实例化此类时自动执行,这也是一件好事。在Java中,它或多或少是您放入构造函数块中的代码。

所以上课:

DelayedInit

无论class Foo { // code. def method = ??? } 是什么,调用code时它将自动执行。

如果有物体

new Foo

object Foo { // code. def method = ??? } 将自动执行,而无需调用code,因为Scala会自动为您提供一个名为new的单例实例。

因此,基本上,如果主体定义中包含任何内容,它将自动执行。您无需显式执行它。

现在具有Foo特性。要知道的一件事是,它为我们提供了一种机制,可以执行所谓的编译器技巧,从而可以重写我们代码的某些部分。这是它可能令人困惑的原因之一。因为当您使用它时,Scala编译器实际执行的不是您正在阅读的代码,而是对其的略微修改。要了解发生了什么,您需要了解编译器更改代码的方式。

DelayedInit特性允许我们执行的技巧是获取属于类/对象定义主体的代码,并将其转换为通过名称传递给参数的参数DelayedInit上定义的delayedInit

基本上,它会重写:

DelayedInit

进入

object Foo {
// some code
} 

这意味着object Foo { // delayedInt({some code}) } 不是自动执行// some code,而是通过将delayedInt作为参数自动传递给它的方法。

因此,扩展了// some code的所有内容都将其初始化代码替换为方法调用DelayedInit,并将初始化代码作为参数传递。因此,为什么没有人需要显式调用delayedInt方法。

现在,让我们来看看如何将其绑定到delayedInt特性以及如何使用App特性为Scala应用程序提供入口点。

您会注意到,App特征上的delayedInit方法没有提供任何实现。因此,DelayedInit被调用时的实际行为需要由扩展delayedInit的其他事物提供。

DelayedInit特性就是这样的实现。 App特性有什么作用?与讨论主题相关的两个重要事项:

  1. 它提供了App的实现,该实现采用了传递的初始化代码,并将其放在delayedInit中。
  2. 它提供了主要方法ListBuffer,该方法满足JVM的要求,即拥有一个带有def main(args: Array[String])的方法作为程序的入口点。这个主要方法所做的就是执行放置在ListBuffer中的任何代码。

public static void main(String[])特质的上述特征意味着,扩展它的任何对象/类都将其初始化代码传递到App,然后将其添加到ListBuffer中,然后将其添加到List /现在,扩展它的类将具有main方法,该方法在调用时(大多数情况下,以JVM作为入口点)将遍历ListBuffer中的代码并执行它。

基本上会变成这样:

delayedInit

进入此

object Foo {
// some code
}

那么为什么要有一个List缓冲区来存储要执行的代码?因为,正如我上面所说,可能有一个类的层次结构,其中层次结构中每个类的初始化代码都作为执行程序的一部分而执行。要查看此操作。

给出以下代码段:

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 
??? 
  }
}

运行时将输出以下内容:

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 AnotherClassAnotherTrait组成的层次结构中的各个初始化代码将通过YetAnotherTrait添加到initCode列表缓冲区中delayedInit特性的方法,然后由App特性提供的main方法执行它们。

您会注意到源代码,已经发现App的整个机制已被弃用,并计划在将来删除。

答案 1 :(得分:0)

delayedInit:-

  

初始化钩子。这样会保存所有初始化代码,以便在其中执行   main。通常永远不会直接从用户代码中调用此方法。   相反,它被称为这些类的编译器生成的代码,并且   从DelayedInit特征继承而来的对象(但不是特征)和   本身并没有定义delayedInit方法。

App scala

delayedInit开始不推荐使用

2.11.0。此外,它还有一些未解决的bugs

初始代码:-

我不确定将initCodes定义为ListBuffer的原因。