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


先把目标说清楚:我想做什么
如果只想快速看到“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的那些“看起来复杂”的特性变成你习惯的工具。继续写代码,继续犯小错误,然后解决它们——这就是成长的路。