撰寫單行程式 僅限 Scala 3
Scala 3 提供定義可從命令列呼叫程式的全新方式:將 @main
註解新增至方法中,即可將其轉換為可執行程式的進入點
@main def hello() = println("Hello, World")
若要執行此程式,請將程式碼行儲存在名為 Hello.scala 的檔案中,檔案名稱不必與方法名稱相符,然後使用 scala
執行。
$ scala Hello.scala
Hello, World
帶有 @main
註解的方法可以寫在頂層(如所示),或寫在靜態可存取的物件內。在任一種情況下,程式名稱都是方法名稱,不包含任何物件前置詞。
透過閱讀以下各節或觀看此影片,進一步了解 @main
註解
命令列引數
使用此方法,您的 @main
方法可以處理命令列引數,而這些引數可以有不同的類型。例如,假設此 @main
方法會接收一個 Int
、一個 String
和一個可變參數 String*
參數
@main def happyBirthday(age: Int, name: String, others: String*) =
val suffix = (age % 100) match
case 11 | 12 | 13 => "th"
case _ => (age % 10) match
case 1 => "st"
case 2 => "nd"
case 3 => "rd"
case _ => "th"
val sb = StringBuilder(s"Happy $age$suffix birthday, $name")
for other <- others do sb.append(" and ").append(other)
println(sb.toString)
當您編譯該程式碼時,它會建立一個名為 happyBirthday
的主程式,呼叫方式如下
$ scala happyBirthday 23 Lisa Peter
Happy 23rd Birthday, Lisa and Peter!
如所示,@main
方法可以有任意數量的參數。對於每個參數類型,都必須有 scala.util.CommandLineParser.FromString
類型類別的 給定實例,它將參數 String
轉換為所需的參數類型。此外,如所示,主方法的參數清單可以以重複參數結束,例如 String*
,它會擷取命令列上提供的其他所有參數。
從 @main
方法實作的程式會檢查命令列上有足夠的參數來填入所有參數,以及參數字串是否可以轉換為所需的類型。如果檢查失敗,程式會終止並顯示錯誤訊息。
$ scala happyBirthday 22
Illegal command line after first argument: more arguments expected
$ scala happyBirthday sixty Fred
Illegal command line: java.lang.NumberFormatException: For input string: "sixty"
使用者定義的類型作為參數
如上所述,編譯器會尋找參數類型 scala.util.CommandLineParser.FromString
類型類別的給定實例。例如,假設您有一個自訂 Color
類型,您想要將其用作參數。您可以像下面這樣執行此操作
enum Color:
case Red, Green, Blue
given CommandLineParser.FromString[Color] with
def fromString(value: String): Color = Color.valueOf(value)
@main def run(color: Color): Unit =
println(s"The color is ${color.toString}")
這適用於程式中的自訂使用者類型,以及您可能從其他函式庫使用的類型。
詳細資訊
Scala 編譯器會從 @main
方法 f
產生一個程式,如下所示
- 它會在找到
@main
方法的套件中建立一個名為f
的類別。 - 該類別有一個靜態方法
main
,其簽章與 Javamain
方法的簽章相同:它將Array[String]
作為參數,並傳回Unit
。 - 產生的
main
方法會呼叫方法f
,並使用scala.util.CommandLineParser.FromString
物件中的方法轉換參數。
例如,上面的 happyBirthday
方法會產生等於下列類別的其他程式碼
final class happyBirthday {
import scala.util.{CommandLineParser as CLP}
<static> def main(args: Array[String]): Unit =
try
happyBirthday(
CLP.parseArgument[Int](args, 0),
CLP.parseArgument[String](args, 1),
CLP.parseRemainingArguments[String](args, 2)*)
catch {
case error: CLP.ParseError => CLP.showError(error)
}
}
注意:在此產生的程式碼中,
<static>
修飾詞表示main
方法會產生為happyBirthday
類別的靜態方法。此功能不適用於 Scala 中的使用者程式。一般「靜態」成員會使用物件而非 Scala 中產生。
與 Scala 2 的向後相容性
@main
方法是建議用來產生程式的方法,這些程式可以在 Scala 3 中從命令列呼叫。它們取代了 Scala 2 中先前的做法,也就是建立一個延伸 App
類別的 object
先前 App
的功能,它依賴於「神奇」DelayedInit
特質,不再可用。App
目前仍以有限的形式存在,但它不支援命令列引數,而且未來將會被棄用。
如果程式需要在 Scala 2 和 Scala 3 之間交叉建置,建議使用具有明確 main
方法的 object
,以及單一 Array[String]
引數
object happyBirthday {
private def happyBirthday(age: Int, name: String, others: String*) = {
... // same as before
}
def main(args: Array[String]): Unit =
happyBirthday(args(0).toInt, args(1), args.drop(2).toIndexedSeq:_*)
}
請注意,我們在此使用
:_*
傳遞變數引數,它保留在 Scala 3 中以維持向後相容性。
如果您將該程式碼置於名為 happyBirthday.scala 的檔案中,您就可以使用 scalac
編譯它,然後使用 scala
執行它,如先前所示
$ scalac happyBirthday.scala
$ scala happyBirthday 23 Lisa Peter
Happy 23rd Birthday, Lisa and Peter!