编写单行程序 仅限 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
方法使用scala.util.CommandLineParser.FromString
对象中的方法转换的参数调用方法f
。
例如,上面的 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 中的用户程序中不可用。常规“static”成员在 Scala 中使用对象而不是使用类生成。
与 Scala 2 的向后兼容性
@main
方法是生成可从 Scala 3 中的命令行调用的程序的推荐方法。它们取代了 Scala 2 中先前的方法,即创建一个扩展 App
类的 object
依赖于“magic”DelayedInit
特性的 App
的先前功能不再可用。目前 App
仍以受限形式存在,但它不支持命令行参数,并且将来会被弃用。
如果程序需要在 Scala 2 和 Scala 3 之间进行交叉构建,建议使用具有显式 main
方法和单个 Array[String]
参数的 object
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:_*)
}
请注意,我们在此处使用
:_*
传递 vararg 参数,该参数保留在 Scala 3 中以实现向后兼容性。
如果将该代码放置在名为 happyBirthday.scala 的文件中,则可以使用 scalac
编译它,并使用 scala
运行它,如前所示
$ scalac happyBirthday.scala
$ scala happyBirthday 23 Lisa Peter
Happy 23rd Birthday, Lisa and Peter!