在斯卡拉; 我应该使用App特质吗?

Mic*_*ill 32 program-entry-point scala traits

我刚刚开始学习Scala,我正在遵循的许多教程都使用了一种main方法的不同表示形式的组合.除了熟悉的主要方法; 还有使用特征AppApplication.它似乎Application已被弃用,不推荐使用,但我找不到任何有关这些定义入口点的方法的信息.

所以,我想知道是否有人可以向我解释:

  • 性格AppApplication工作如何?
  • 为什么Application不再推荐这个App特性,这个特性有什么不同呢?
  • 我应该在哪里使用传统的主要方法,何时应该使用它App来启动我的程序?这两种方法有什么区别?

Raf*_*ter 28

特征的问题Application实际上在其文档中描述:

(1)引用该对象的线程代码将阻塞,直到静态初始化完成.但是,因为扩展Application的对象的整个执行发生在静态初始化期间,所以如果并发代码必须与封闭对象同步,则它们将始终死锁.

这是一个棘手的问题.如果扩展Application特征,基本上就是创建一个Java类:

class MyApplication implements Application {
  static {
    // All code goes in here
  }
}
Run Code Online (Sandbox Code Playgroud)

JVM运行上面隐式在MyApplication类上同步的类初始化程序.这样,可以确保MyApplication在初始化类之前不会创建任何实例.如果从应用程序生成一个再次需要访问其实例的线程,则应用MyApplication程序将无法锁定,因为类初始化仅在整个程序执行完毕后才完成.这意味着一个矛盾,因为只要您的程序正在运行,就不能创建任何实例.

(2)如上所述,无法获取命令行参数,因为扩展Application的对象体中的所有代码都是作为静态初始化的一部分运行的,这是在Application的main方法甚至开始执行之前发生的.

类初始值设定项不接受任何参数.此外,它首先运行,然后可以将任何值传递给类,因为在您甚至可以分配静态字段值之前需要执行类初始化程序.因此,args您通常在main方法上收到的内容将丢失.

(3)静态初始化程序在程序执行期间只运行一次,而JVM作者通常认为它们的执行时间相对较短.因此,某些JVM配置可能会变得混乱,或者只是无法优化或JIT扩展Application的对象体中的代码.这可能导致显着的性能下降.

JVM优化了经常运行的代码.这样,它确保不会浪费任何不是性能瓶颈的方法的运行时间.但是,它安全地假定static方法只执行一次,因为它们无法手动调用.因此,它不会优化从类初始化程序运行的main代码,如果您正在使用该Application特征,则该类初始化程序是应用程序的方法代码.

App特征通过扩展来解决所有这些问题DelayedInit.Scala编译器明确知道此特征,因此初始化代码不是从类初始化程序运行,而是从另一个方法运行.请注意名称引用是特征的唯一方法:

trait Helper extends DelayedInit {
  def delayedInit(body: => Unit) = {
    println("dummy text, printed before initialization of C")
    body
  }
}
Run Code Online (Sandbox Code Playgroud)

实现时DelayedInit,Scala编译器将其实现类或对象的任何初始化代码包装到for name函数中,然后传递给该delayedInit方法.不直接执行初始化代码.这样,您还可以在运行初始化程序之前运行代码,这样可以让Scala将应用程序的运行时指标打印到控制台,该控制台包含在程序的入口点和退出处.不过,也有这种做法的一些注意事项和使用DelayedInit,因此不推荐使用.你应该真正只依赖于App解决特质所带来的问题的Application特性.你不应该DelayedInit直接实施.

main如果你愿意,你仍然可以定义一个方法,只要你在一个方法中定义它object.这主要是风格问题:

object HelloWorld {
  def main(args: Array[String]) {
    println("Hello, world!")
  }
}
Run Code Online (Sandbox Code Playgroud)