4.4 Scala应用程序
要运行一个Scala程序,必须提供一个独立对象的名称。这个独立对象需要包含一个main方法,该方法接收一个Array[String]作为参数,结果类型为Unit。任何带有满足正确签名的main方法的独立对象都能被用作应用程序的入口。如示例4.3所示:
示例4.3 Summer应用程序
示例4.3中单例对象的名称是Summer。它的main方法带有正确的签名,因此可以将它当作应用程序来使用。文件中的第一条语句引入了前一例的ChecksumAccumulator对象中定义的calculate方法。这句引入让你可以在这个文件后续的代码中使用这个方法的简单名称。[5]main方法的方法体只是简单地打印出每个参数,以及参数的校验和,以冒号分隔开。
注意
Scala在每一个Scala源码文件都隐式地引入了java.lang和scala包的成员,以及名为Predef的单例对象的所有成员。位于scala包的Predef包含了很多有用的方法。比如,当你在Scala源码中使用println时,实际上调用了Predef的println(Predef.println转而调用Console.println,执行具体的操作)。而当你写下assert时,实际上是调用了Predef.assert。
要运行Summer这个应用程序,可以把示例4.3中的代码放入名为Summer.scala的文件中。因为Summer也用到ChecksumAccumulator,将示例4.1中的类和示例4.2中的伴生对象放入名为ChecksumAccumulator.scala的文件中。
Scala和Java的区别之一,是Java要求你将公共的类放入跟类同名的文件中(例如需要将SpeedRacer类放到SpeedRacer.java中),而在Scala中可以任意命名.scala文件,不论你放什么类或代码到这个文件中。不过,通常对于那些非脚本的场景,把类放入以类名命名的文件是推荐的做法,就像Java那样,以便程序员能够更容易地根据类名定位到对应的文件。这也是我们在命名Summer.scala和ChecksumAccumulator.scala时所采取的策略。
ChecksumAccumulator.scala和Summer.scala都不是脚本,因为它们都是以定义结尾的。而脚本则不同,必须以一个可以计算出结果的表达式结尾。因此,如果你尝试以脚本的方式运行Summer.scala,解释器会报错,提示你Summer.scala并不以一个结果表达式结尾(当然这是假定你并没有在Summer对象定义之后自己再添加任何额外的表达式)。需要用Scala编译器实际编译这些文件,然后运行编译出来的类。编译的方式之一,是使用scalac这个基础的Scala编译器,就像这样:
这将编译你的源文件,不过在编译结束之前,你可能会注意到一个比较明显的延迟。这是因为每一次编译器启动,它都会花时间扫描jar文件的内容以及执行其他一些初始化工作,然后才开始关注你提交给它的新的源码文件。因为这个原因,Scala的分发包还包含了一个名为fsc的Scala编译器的守护进程(daemon)。使用的方式如下:
第一次运行fsc,它会创建一个本地的服务器守护进程,绑定到计算机的某个端口上。然后,它会通过这个端口将需要编译的文件发送给这个守护进程。下一次运行fsc的时候,这个守护进程已经在运行了,所以fsc会简单地将文件清单发给这个守护进程,然后守护进程就会立即编译这些文件。使用fsc,只有在首次运行时才需要等待Java运行时启动。如果你想要停止fsc这个守护进程,可以执行fsc -shutdown。
不论是运行scalac还是fsc命令,都会产出Java类文件,这些类文件可以用scala命令来运行,这个命令也是你在之前的示例中用来执行解释器的那一个。不过,跟之前我们运行那些带有Scala代码的.scala文件不同,[6]在这个使用场景下,你给它的是那个包含了符合正确签名要求的main方法的独立对象的名字。因此,可以用下面的命令来运行Summer这个应用程序:
你将看到这个程序打印出了传入的两个命令行参数对应的校验和: