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

时间:2014-06-26 18:12:53

标签: scala main traits

我刚刚开始学习Scala,我正在学习的许多教程都使用了main方法的不同表示形式的组合。除了熟悉的主要方法;还有使用特征AppApplication。 看来Application已被弃用,不推荐使用,但除了这些定义入口点的方法之外,我找不到任何可解释的信息。

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

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

1 个答案:

答案 0 :(得分:25)

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

  

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

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

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

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

  

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

类初始值设定项不接受任何参数。此外,它首先运行,然后可以将任何值传递给类,因为在您甚至可以分配静态字段值之前需要执行类初始化程序。因此,您通常在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
  }
}

在实现DelayedInit时,Scala编译器将其实现类或对象的任何初始化代码包装到 for name 函数中,然后将其传递给delayedInit方法。不直接执行初始化代码。这样,您还可以在运行初始化程序之前运行代码,这样可以让Scala将应用程序的运行时指标打印到控制台,该控制台包含在程序的入口点和出口处。但是,有some caveats of this approach,因此不推荐使用DelayedInit。你应该只依靠App特性来解决Application特征带来的问题。您不应直接实施DelayedInit

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

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