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