我刚刚开始学习Scala,下面的许多教程都在为main
方法使用不同表示的组合。除了熟悉的主要方法;还使用了性状CCD_ 2或CCD_。Application
似乎已经被弃用,不推荐使用,但我找不到任何信息来解释除此之外的定义入口点的每种方法。
所以,我想知道是否有人可以向我解释:
- 特征
App
和Application
是如何工作的 - 为什么不再推荐特性
Application
?App
特性的作用有何不同 - 我应该在哪里使用传统的main方法,什么时候应该使用
App
来启动我的程序?这两种方法有什么区别
Application
特性的问题实际上在其文档中进行了描述:
(1( 引用对象的线程化代码将被阻塞,直到静态初始化完成。但是,由于对象扩展应用程序的整个执行都发生在静态初始化期间,因此如果并发代码必须与封闭对象同步,则它将始终处于死锁状态。
这是一个棘手的问题。如果扩展Application
特性,那么基本上就是在创建一个Java类:
class MyApplication implements Application {
static {
// All code goes in here
}
}
JVM在MyApplication
类上运行隐式同步的上述类初始化器。通过这种方式,可以确保在初始化MyApplication
的类之前不会创建任何实例。如果您从应用程序中派生出一个线程,该线程再次需要访问MyApplication
的实例,那么您的应用程序将死锁,因为类初始化只有在整个程序执行后才能完成。这意味着一个悖论,因为只要程序在运行,就无法创建任何实例。
(2( 如上所述,无法获得命令行参数,因为对象扩展应用程序主体中的所有代码都是作为静态初始化的一部分运行的,静态初始化发生在应用程序的主方法开始执行之前。
类初始值设定项不接受任何参数。此外,它是首先运行的,在任何值都可以传递给类之前,因为在您甚至可以分配静态字段值之前,需要执行类初始值设定项。因此,您通常通过main
方法接收的args
将丢失。
(3( 静态初始化程序在程序执行期间只运行一次,JVM作者通常认为它们的执行时间相对较短。因此,某些JVM配置可能会变得混乱,或者根本无法优化或JIT对象扩展应用程序主体中的代码。这可能会导致性能显著下降。
JVM优化了频繁运行的代码。通过这种方式,它确保不会将运行时间浪费在那些不是真正的性能瓶颈的方法上。然而,它安全地假设static
方法只执行一次,因为它们不能手动调用。因此,它不会优化从类初始化器运行的代码,但如果您使用Application
特性,则类初始化器是应用程序的main
方法代码。
CCD_ 20特性通过扩展CCD_。Scala编译器明确知道这种特性,因此初始化代码不是从类初始值设定项运行的,而是从另一个方法运行的。注意对名称的引用,该引用被屏蔽到特征的唯一方法:
trait Helper extends DelayedInit {
def delayedInit(body: => Unit) = {
println("dummy text, printed before initialization of C")
body
}
}
在实现App
2时,Scala编译器将其实现类或对象的任何初始化代码封装到名称的函数中,然后将其传递给delayedInit
方法。不直接执行初始化代码。这样,您还可以在初始化器运行之前运行代码,例如,Scala可以将应用程序的运行时指标打印到控制台,控制台围绕程序的入口点和出口。但是,这种方法有一些注意事项,因此不赞成使用DelayedInit
。你真的应该只依赖App
特质,它解决了Application
特质带来的问题。您不应该直接实现DelayedInit
。
如果需要,您仍然可以定义main
方法,只要您在object
中定义它即可。这主要是一个风格问题:
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}