HelloWorld 与 Scala 使用教程

要在Scala中运行HelloWorld,先安装JDK与Scala工具链(推荐使用sbt或scala-cli),然后用object定义main方法或继承App,或在Scala 3使用@main注解;可用REPL交互、sbt或IDE(如IntelliJ)开发,编译打包为jar或用GraalVM生成原生可执行文件,调试与依赖管理通过sbt完成,日常用法以清晰的模块划分与不可变数据为优先,性能与可维护性并重。

HelloWorld 与 Scala 使用教程

HelloWorld 与 Scala 使用教程

先把目标说清楚:我想做什么

如果只想快速看到“Hello, world!”那就是三步:安装运行时、写一句打印、运行它。要把它当成项目来开发,则要考虑构建工具、依赖管理、测试和部署。把复杂拆成小块,每块都能解释清楚,这是费曼法的精神——教会别人之前先能自己说清楚。

环境准备(JDK、Scala 与工具链)

JDK

需要一台有JDK的机器。Scala 2.13通常搭配Java 8/11/17都能工作,Scala 3(Dotty)推荐使用Java 11或更高版本。常见做法是安装OpenJDK(例如Adoptium或Azul)。安装后用命令检查:

  • java -version
  • javac -version

Scala 运行时与编译

你可以直接安装Scala二进制,或更常见地使用构建工具来隐式管理Scala版本:

  • sbt:Scala 标准构建工具,支持依赖、测试、打包。
  • scala-cli:一体化脚本及构建工具,适合快速原型与脚本式开发。
  • Ammonite:交互脚本环境,更友好于REPL。

HelloWorld 的几种写法(从简单到规范)

下面用几种方式来展示“Hello, world!”:最简单的交互式、标准main入口、Scala 3的@main注解。

REPL(交互式)

打开终端,输入 scala(或 amm),然后直接写:

scala> println("Hello, world!")

传统 main(Scala 2 与 3 都可)

文件 Hello.scala:

object Hello {
  def main(args: Array[String]): Unit =
    println("Hello, world!")
}

编译并运行:

  • scalac Hello.scala
  • scala Hello

继承 App(快捷但需注意)

object HelloApp extends App {
  println("Hello, world!")
}

注意:App 会在后台生成主方法并处理 args,但在复杂初始化时可能带来顺序问题,不建议在大型项目中滥用。

Scala 3 的 @main(更现代)

@main def hello(): Unit =
  println("Hello, world!")

用 sbt 建立一个小项目(推荐流程)

如果你准备长期维护代码,用 sbt 很合适。下面一步步来:

  • 创建目录结构:
    • project/
    • src/main/scala/
    • src/test/scala/
  • 在项目根目录写 build.sbt,例如:
    name := "hello"
    version := "0.1.0"
    scalaVersion := "2.13.12"
  • 把 Hello.scala 放在 src/main/scala/ 下,执行 sbt run。

常用 sbt 命令速览

命令 作用
sbt compile 编译源代码
sbt run 运行主类
sbt test 运行测试
sbt package 打包为jar(不包含依赖)

Scala 的核心概念(用费曼法解释)

要学一门语言,别死记语法,先抓住它的核心思想:

  • 不可变优先(Immutability):用 val 而不是 var,把可变性限制在边界。
  • 函数是“第一等值”:函数可以赋值、传参、返回,表达组合更容易。
  • 表达式导向(expression-oriented):大多数结构都有返回值,可以链式组合。
  • 面向对象 + 函数式的融合:既有类、继承,也有高阶函数、模式匹配、不可变数据。

示例:val vs var、函数与匿名函数

val x = 1          // 不可变
var y = 2          // 可变
def add(a: Int, b: Int): Int = a + b
val f = (n: Int) => n * 2

样例类(case class)与模式匹配

case class Person(name: String, age: Int)

def greet(p: Person): String = p match {
  case Person(n, a) if a >= 18 => s"Hello $n, adult"
  case Person(n, _) => s"Hi $n"
}

常见功能点:集合、并发、互操作

集合

Scala 的集合分为可变与不可变,默认使用不可变集合(scala.collection.immutable)。常见操作:map、filter、flatMap、foldLeft。例如:

val nums = List(1,2,3)
val doubled = nums.map(_ * 2)  // List(2,4,6)

并发(Future)

最简单的异步工具是 Future,配合 ExecutionContext 使用:

import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.util.{Success, Failure}

val f = Future { Thread.sleep(500) 42 } f.onComplete { case Success(v) => println(s"Got $v") case Failure(e) => println("Error: " + e) }

注意:Production 中不要使用 global 为所有任务共享线程池,应该根据任务类型自定义 ExecutionContext。

与 Java 的互操作

Scala 可以直接使用 Java 类库,互操作性非常强。举例:用 java.util.ArrayList 或者 JDBC 驱动都毫无障碍。Scala 对 Java 的泛型、异常、集合转换提供了便利工具(如 JavaConverters)。

打包与部署

把Scala程序部署到生产环境常见方式:

  • 打包成fat/uber jar(使用 sbt-assembly)并用 java -jar 运行。
  • 用 Docker 把运行时与jar一起打包,镜像部署。
  • 用 GraalVM 的 native-image 生成本地可执行文件以减少启动时间(适合短时任务或小服务)。

调试与常见陷阱

  • 隐式转换/参数(Scala 2 的 implicit、Scala 3 的 given/using):很强大但可能让代码难以理解,遇到问题先查隐式来源。
  • 初始化顺序问题:对象的初始化顺序会影响在构造过程中访问未初始化字段的行为,尤其是继承链复杂时。
  • 二进制兼容与Scala版本:Scala 的二进制兼容不像Java那样稳定,从2.11到2.12、2.12到2.13之间可能需要重新编译依赖;Scala 3 与 Scala 2 的兼容性也需要注意。

学习路线与工具推荐

如果你刚接触Scala,建议按这个顺序:

  • 学会用 REPL 做交互探索(理解表达式、类型推断)。
  • 写一个简单的 sbt 项目,熟悉构建流程与依赖管理。
  • 掌握集合、函数式变换、case class 与模式匹配。
  • 学习 Future/Akka/Http 等实践工具,做一个小服务。
  • 关注性能调优(JVM调优、GC、内存与序列化)。

工具

  • 编辑器:IntelliJ IDEA(带Scala插件)是主流选择,VS Code 配合 Metals 也很流行。
  • 构建:sbt、scala-cli、Mill(非主流但有时用到)。
  • 交互/脚本:Ammonite、Scala REPL。

示例项目结构速览

一个典型的小项目结构:

hello/
  build.sbt
  project/
  src/
    main/
      scala/
        Hello.scala
    test/
      scala/
        HelloSpec.scala

常见问题解答(FAQ 快速扫)

  • Scala 和 Java 性能比如何? 在JVM上,性能差异主要来自算法实现和JVM配置,Scala 的抽象(如匿名函数、集合转换)可能引入短期分配开销,合理使用(避免不必要的中间集合)即可。
  • 我应该学 Scala 2 还是 Scala 3? 新项目优先考虑 Scala 3;如果依赖大量尚未迁移的库,可在 Scala 2.13 上开发并逐步迁移。
  • 何时用 mutable 结构? 仅当性能热点、并发或与特定API互操作必须时使用,默认偏向不可变。

好吧,就像写代码一样,讲明白也会时不时卡住——你可以先照着上面的步骤做一个HelloWorld,然后把每一步拆开试验:REPL里玩表达式、sbt里试依赖、IDE里断点调试。学会把问题拆分,遇到错误时先从最小可复现例子开始,这样你会慢慢把Scala的那些“看起来复杂”的特性变成你习惯的工具。继续写代码,继续犯小错误,然后解决它们——这就是成长的路。