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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24437423/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 06:22:29  来源:igfitidea点击:

In Scala; should I use the App trait?

scalamaintraits

提问by Micheal Hill

I've just started learning Scala and many of the tutorials that I'm following are using a combination of different representations for a mainmethod. Aside from the familiar main method; there's also the use of traits Appor Application. It appears that Applicationhas been deprecated and is not recommended, but I can't find any information that explains much beyond this about each of these ways to define an entry point.

我刚刚开始学习 Scala,我所关注的许多教程都使用了一种main方法的不同表示的组合。除了熟悉的 main 方法;还有特征App或的使用Application。似乎Application已被弃用且不推荐使用,但我找不到任何信息来解释这些定义入口点的方法中的每一种。

So, I'm wondering if someone could explain to me:

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

  • How do the traits Appand Applicationwork?
  • Why is the trait Applicationno longer recommended and what does the Apptrait do that is different?
  • Where should I use the traditional main method and when should I use Appto start my program? What's the difference between the two approaches?
  • 特性AppApplication作用如何?
  • 为什么Application不再推荐该Apptrait,该trait有何不同?
  • 我应该在哪里使用传统的 main 方法,我应该什么时候使用它App来启动我的程序?这两种方法有什么区别?

采纳答案by Rafael Winterhalter

The problem with the Applicationtrait is actually described in its documentation:

Applicationtrait的问题实际上在它的文档中有描述:

(1) Threaded code that references the object will block until static initialization is complete. However, because the entire execution of an object extending Application takes place during static initialization, concurrent code will always deadlock if it must synchronize with the enclosing object.

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

This is a tricky one. If you extend the Applicationtrait, you are basically creating a Java class:

这是一个棘手的问题。如果您扩展Applicationtrait,您基本上是在创建一个 Java 类:

class MyApplication implements Application {
  static {
    // All code goes in here
  }
}

The JVM runs the above class initializer implicitly synchronized on the MyApplicationclass. This way, it is assured that no instance of MyApplicationis created before its class is initialized. If you spawn a thread from your application that again needs to access an instance of MyApplication, your application would dead lock as the class initialization is only complete after the entire program has executed. This implies a paradox as no instance can be created as long as your program is running.

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

(2) As described above, there is no way to obtain the command-line arguments because all code in body of an object extending Application is run as part of the static initialization which occurs before Application's main method even begins execution.

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

A class initializer does not take any arguments. Also, it is run first, before any values could be handed to the class as the class initializer needs to be executed before you could even assign a static field value. Thus, the argsthat you normally receive on a mainmethod are lost.

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

(3) Static initializers are run only once during program execution, and JVM authors usually assume their execution to be relatively short. Therefore, certain JVM configurations may become confused, or simply fail to optimize or JIT the code in the body of an object extending Application. This can lead to a significant performance degradation.

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

The JVM optimizes code that is run frequently. This way, it makes sure that no run time is wasted on methods that are not really a performance bottle neck. However, it safely assumes that staticmethods are only executed once as they cannot be invoked manually. Thus, it will not optimize the code that is run from a class initializer which is however your application's mainmethod code if you are using the Applicationtrait.

JVM 优化频繁运行的代码。这样,它可以确保不会在不是真正性能瓶颈的方法上浪费运行时间。然而,它安全地假设static方法只执行一次,因为它们不能被手动调用。因此,它不会优化从类初始值设定项运行的代码,但main如果您使用该Application特征,则该代码是您的应用程序的方法代码。

The Apptrait fixes all this by extending DelayedInit. This trait is explicitly known to the Scala compiler such that initialization code is not run from a class initializer but from another method. Note the for namereference that is haded to the trait's only method:

App特性通过扩展DelayedInit. Scala 编译器明确知道此特征,因此初始化代码不是从类初始化程序运行,而是从另一个方法运行。请注意对trait 的唯一方法的名称引用:

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

When implementing DelayedInit, the Scala compiler wraps any initialization code of its implementing class or object into a for namefunction which is then passed to the delayedInitmethod. No initialization code is executed directly. This way, you can also run code before an initializer is run what allows Scala for example to print an application's runtime metrics to the console which is wrapped around the program's entry point and exit. However, there are some caveats of this approachand using DelayedInitis therefore deprecated. You should really only rely on the Apptrait which solves the problems that are imposed by the Applicationtrait. You should not implement DelayedInitdirectly.

在实现时DelayedInit,Scala 编译器将其实现类或对象的任何初始化代码包装到一个for name函数中,然后将其传递给该delayedInit方法。没有直接执行初始化代码。这样,您还可以在运行初始化程序之前运行代码,例如允许 Scala 将应用程序的运行时指标打印到控制台,控制台围绕程序的入口点和出口进行包装。不过,也有这种做法的一些注意事项和使用DelayedInit,因此不推荐使用。你真的应该只依赖于App解决由Applicationtrait强加的问题的trait。您不应该DelayedInit直接实施。

You can still define a mainmethod if you want to, as long as you define it in an object. This is mostly a matter of style:

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

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