Scala 3 — 书籍

Scala 3 中的主要方法

语言
编写单行程序 仅限 Scala 3

Scala 3 提供了一种定义可从命令行调用的程序的新方法:向方法添加 @main 注释可将其转换为可执行程序的入口点

@main def hello() = println("Hello, World")

要运行此程序,请将代码行保存在名为 Hello.scala 的文件中(文件名不必与方法名匹配),然后使用 scala 运行它

$ scala Hello.scala
Hello, World

带有 @main 注释的方法可以编写在顶层(如所示),也可以编写在静态可访问对象内。在任何一种情况下,程序的名称都是方法的名称,没有任何对象前缀。

通过阅读以下部分或观看此视频,了解有关 @main 注释的更多信息

命令行参数

使用此方法,您的 @main 方法可以处理命令行参数,并且这些参数可以具有不同的类型。例如,给定此 @main 方法,它采用 IntString 和可变参数 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,其签名与 Java main 方法的签名相同:它将 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!

此页面的贡献者