Scala程序及其Application特質(zhì)
要執(zhí)行Scala程序,你一定要提供一個(gè)有main方法(僅帶一個(gè)參數(shù),Array[String],且結(jié)果類型為Unit)的孤立單例對(duì)象名。任何擁有合適簽名的main方法的單例對(duì)象都可以用來(lái)作為程序的入口點(diǎn)。代碼4.3展示了一個(gè)例子:
51CTO編輯推薦:Scala編程語(yǔ)言專題
- // 文件Summer.scala
- import ChecksumAccumulator.calculate
- object Summer {
- def main(args: Array[String]) {
- for (arg < - args)
- println(arg + ": " + calculate(arg))
- }
- }
代碼 4.3 程序Summer
代碼4.3單例對(duì)象的名字是Summer。它的main方法具有合適的簽名,所以你可以把它用作程序。文件中的第一個(gè)語(yǔ)句是引用定義在前例中ChecksumAccumulator對(duì)象中的calculate方法。這個(gè)引用語(yǔ)句允許你在文件之后的部分里使用方法的簡(jiǎn)化名。如果你是Java程序員,你可以認(rèn)為這種引用類似于Java 5引入的精通引用特性。然而Scala里的一個(gè)不同是,你可以從任何對(duì)象引用成員,而不只是單例對(duì)象。main方法體簡(jiǎn)單地打印輸出每個(gè)參數(shù)和參數(shù)的校驗(yàn)和,用冒號(hào)分隔。
注意
Scala隱式引用了包java.lang和scala的成員,和名為Predef的單例對(duì)象的成員,到每個(gè)Scala源文件中。Predef,被放置在包scala中,包含了許多有用的方法。例如,當(dāng)在Scala源文件中寫(xiě)pringln的時(shí)候,你實(shí)際調(diào)用了Predef的println。(Predef.pringln運(yùn)轉(zhuǎn)并調(diào)用Console.println,做實(shí)際的工作。)當(dāng)你寫(xiě)assert,你是在調(diào)用Predef.assert。
要執(zhí)行Summer應(yīng)用程序,把代碼4.3的代碼放在文件Summer.scala中。因?yàn)镾ummer使用了ChecksumAccumulator,把ChecksumAccumulator的代碼,包括代碼4.1的類和代碼4.2里它的伴生對(duì)象,放在文件ChecksumAccumulator.scala中。
Scala和Java之間有一點(diǎn)不同,Java需要你在跟著類命名的文件里放上一個(gè)公共類——如文件SpeedRacer.java里要放上類SpeedRacer——Scala里,你可以任意命名.scala文件,而不用考慮里面放了什么Scala類或代碼。然而通常情況下如果不是腳本,推薦的風(fēng)格是像在Java里那樣按照所包含的類名來(lái)命名文件,這樣程序員就可以通過(guò)查看文件名的方式更容易地找到類。這就是我們?cè)诒纠形募﨏hecksumAccumulator.scala和Summer.scala上使用的方式。
無(wú)論ChecksumAccumulator.scala還是Summer.scala都不是腳本,因?yàn)樗麄兪且远x結(jié)束的。反過(guò)來(lái)說(shuō),腳本必然以一個(gè)結(jié)果表達(dá)式結(jié)束。因此如果你嘗試以腳本方式執(zhí)行Summer.scala,Scala解釋器將會(huì)報(bào)錯(cuò)說(shuō)Summer.scala不是以結(jié)果表達(dá)式結(jié)束的(當(dāng)然前提是你沒(méi)有在Summer對(duì)象定義之后加上任何你自己的表達(dá)式)。正確的做法是,你需要用Scala編譯器真正地編譯這些文件,然后執(zhí)行輸出的類文件。其中一種方式是使用scalac,Scala的基本編譯器。輸入:
- $ scalac ChecksumAccumulator.scala Summer.scala
這將編譯你的源文件,不過(guò)在編譯完成之前或許會(huì)有一個(gè)可感知的停頓。原因是每次編譯器啟動(dòng)時(shí),都要花一些時(shí)間掃描jar文件內(nèi)容,并在即使你提交的是新的源文件也在查看之前完成其他初始化工作。因此,Scala的發(fā)布包里還包括了一個(gè)叫做fsc(快速Scala編譯器)的Scala編譯器后臺(tái)服務(wù):daemon。你可以這樣使用:
- $ fsc ChecksumAccumulator.scala Summer.scala
第一次執(zhí)行fsc時(shí),會(huì)創(chuàng)建一個(gè)綁定在你計(jì)算機(jī)端口上的本地服務(wù)器后臺(tái)進(jìn)程。然后它就會(huì)把文件列表通過(guò)端口發(fā)送給后臺(tái)進(jìn)程去編譯,后臺(tái)進(jìn)程完成編譯。下一次你執(zhí)行fsc時(shí),后臺(tái)進(jìn)程就已經(jīng)在運(yùn)行了,于是fsc將只是把文件列表發(fā)給后臺(tái)進(jìn)程,它會(huì)立刻開(kāi)始編譯文件。使用fsc,你只需要在第一次等待Java運(yùn)行時(shí)環(huán)境的啟動(dòng)。如果想停止fsc后臺(tái)進(jìn)程,可以執(zhí)行fsc -shutdown來(lái)關(guān)閉。
不論執(zhí)行scalac還是fsc命令,都將創(chuàng)建Java類文件,然后你可以用scala命令,就像之前的例子里調(diào)用解釋器那樣運(yùn)行它。不過(guò),不是像前面每個(gè)例子里那樣把包含了Scala代碼的帶有.scala擴(kuò)展名的文件交給它解釋執(zhí)行,scala程序用來(lái)“解釋”Scala源文件的真正機(jī)制是,它把Scala源碼編譯成字節(jié)碼,然后立刻通過(guò)類裝載器裝載它們,并執(zhí)行它們。在這里你要給它包含了正確簽名的main方法的孤立對(duì)象名。因此,你可以這樣運(yùn)行Summer應(yīng)用程序:
- $ scala Summer of love
你會(huì)看到兩個(gè)命令行參數(shù)的校驗(yàn)和被打印出來(lái):
- of: -213
- love: -182
Application特質(zhì)
Scala提供了一個(gè)特質(zhì),scala.Application,可以節(jié)省你一些手指的輸入工作。盡管我們還沒(méi)有完全提供給你去搞明白它如何工作的所有需要知道的東西,不過(guò)我們還是認(rèn)為你可能想要知道它。代碼4.4展示了一個(gè)例子:
- import ChecksumAccumulator.calculate
- object FallWinterSpringSummer extends Application {
- for (season < - List("fall", "winter", "spring"))
- println(season +": "+ calculate(season))
- }
代碼 4.4 使用Application特質(zhì)
使用這個(gè)特質(zhì)的方法是,首先在你的單例對(duì)象名后面寫(xiě)上“extends Application” 。然后代之以main方法,你可以把想要放在main方法里的代碼直接放在單例對(duì)象的大括號(hào)之間。就這么簡(jiǎn)單。之后可以像對(duì)其它程序那樣編譯和運(yùn)行。
這種方式之所以能奏效是因?yàn)樘刭|(zhì)Application聲明了帶有合適的簽名的main方法,并由你的單例對(duì)象繼承,使它可以像個(gè)Scala程序那樣用。大括號(hào)之間的代碼被收集進(jìn)了單例對(duì)象的主構(gòu)造器:primary constructor,并在類被初始化時(shí)被執(zhí)行。如果你不明白所有這些指的是什么也不用著急。之后的章節(jié)會(huì)解釋這些,目前可以暫時(shí)不求甚解。
繼承自Application比寫(xiě)個(gè)顯式的main方法要短,不過(guò)它也有些缺點(diǎn)。首先,如果想訪問(wèn)命令行參數(shù)的話就不能用它,因?yàn)閍rgs數(shù)組不可訪問(wèn)。比如,因?yàn)镾ummer程序使用了命令行參數(shù),所以它必須帶有顯式的main方法,如代碼4.3所示。第二,因?yàn)槟承㎎VM線程模型里的局限,如果你的程序是多線程的就需要顯式的main方法。最后,某些JVM的實(shí)現(xiàn)沒(méi)有優(yōu)化被Application特質(zhì)執(zhí)行的對(duì)象的初始化代碼。因此只有當(dāng)你的程序相對(duì)簡(jiǎn)單和單線程情況下你才可以繼承Application特質(zhì)。
【相關(guān)閱讀】