我刚刚开始学习Scala,我正在学习的许多教程都使用了main
方法的不同表示形式的组合。除了熟悉的主要方法;还有使用特征App
或Application
。
看来Application
已被弃用,不推荐使用,但除了这些定义入口点的方法之外,我找不到任何可解释的信息。
所以,我想知道是否有人可以向我解释:
App
和Application
如何运作?Application
以及App
特性有何不同?App
启动我的程序?这两种方法有什么区别?答案 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!")
}
}