Scala 3(代号 Dotty)是 Scala 语言自诞生以来最重大的一次升级,于 2021 年正式发布稳定版。它在语言设计、类型系统、语法简化等方面做出了革命性的改进,同时保持了与 Scala 2 的高度二进制兼容性。本文将深入解析 Scala 3 的核心新特性,并提供从 Scala 2 迁移的实用指南。

一、Scala 3 的设计哲学与核心变化
Scala 3 的设计目标可以概括为三个词:更简洁、更安全、更易学。Martin Odersky 带领团队对语言进行了全面的重新设计,解决了 Scala 2 中长期存在的痛点。
Scala 3 引入了全新的编译器 Dotty,它采用了基于依赖类型(Dependent Types)的 DOT(Dependent Object Types)理论模型,使得类型系统更加严谨和强大。与 Scala 2 的编译器相比,Dotty 在编译速度上也有显著提升。
以下是 Scala 3 最重要的架构级变化:
- Trait 参数化:Trait 现在可以接受构造参数,替代了早期初始化(Early Initializer)的用法
- 枚举(Enum)原生支持:不再需要 sealed trait + case class 的样板代码
- Union Types 和 Intersection Types:替代 Scala 2 中的 Either 和 with 类型
- 上下文抽象重构:given/using 替代了隐式参数和隐式转换
- 可选花括号:基于缩进的语法,让代码更接近 Python 的风格
二、类型系统的重大升级
2.1 Union Types(联合类型)
Union Types 是 Scala 3 最受期待的特性之一。它允许你将多个类型用 | 连接,表示”要么是 A,要么是 B”的语义。
// Scala 3 Union Types
def process(input: String | Int): String = input match {
case s: String => s"字符串: $s"
case i: Int => s"数字: $i"
}
// 编译器自动推断联合类型
val result: String | Int = if (true) "hello" else 42
// 类型缩减 - 公共超类型自动推断
def describe(x: Int | String | Double): String = x.toString
与 Scala 2 中使用 Either 相比,Union Types 不需要包装(wrapping),直接操作原始类型,代码更加简洁直观。
2.2 Intersection Types(交叉类型)
Intersection Types 使用 & 表示一个值同时满足多个类型的约束,替代了 Scala 2 中 with 关键字:
trait Greetable { def greet: String }
trait Loggable { def log(msg: String): Unit }
// Intersection Type
def createEntity: Greetable & Loggable = new Greetable & Loggable {
def greet: String = "Hello!"
def log(msg: String): Unit = println(s"[LOG] $msg")
}
2.3 不透明类型别名(Opaque Type Aliases)
不透明类型别名是 Scala 3 引入的零开销抽象机制。在定义域内,不透明类型等同于底层类型;在外部,它们被视为完全不同的类型,从而实现编译期类型安全且不产生运行时开销。
object Measurement {
opaque type Meters = Double
opaque type Kilometers = Double
object Meters {
def apply(d: Double): Meters = d
}
object Kilometers {
def apply(d: Double): Kilometers = d
}
extension (m: Meters) {
def toKm: Kilometers = m / 1000.0
def value: Double = m
}
}
import Measurement._
val distance = Meters(1500.0)
println(distance.toKm.value) // 1.5
// val wrong: Kilometers = distance // 编译错误!类型不匹配
这在领域驱动设计(DDD)中特别有用,可以避免将不同物理量混用导致的 bug。
三、上下文抽象的全面重构
3.1 Given 和 Using
Scala 3 对隐式机制进行了彻底的重新设计。implicit val 被 given 替代,implicit def 参数被 using 替代。新的命名更加语义化,降低了学习门槛。
// Scala 3 的 given/using
trait Ord[T] {
def compare(x: T, y: T): Int
}
given Ord[Int] with {
def compare(x: Int, y: Int): Int = x - y
}
given Ord[String] with {
def compare(x: String, y: String): Int = x.compareTo(y)
}
// using 关键字替代 implicit 参数
def sort[T](list: List[T])(using ord: Ord[T]): List[T] = {
list.sorted((a, b) => ord.compare(a, b))
}
// summon 替代 implicitly
val intOrd = summon[Ord[Int]]
3.2 Extension Methods(扩展方法)
Scala 3 的扩展方法为类型添加新行为提供了更清晰的语法,替代了 Scala 2 中的隐式类(implicit class)模式:
// Scala 3 Extension Methods
extension (s: String) {
def isPalindrome: Boolean = s == s.reverse
def wordCount: Int = s.split("\\s+").length
def truncate(maxLen: Int): String =
if (s.length <= maxLen) s else s.take(maxLen) + "..."
}
// 对泛型类型的扩展
extension [T](list: List[T]) {
def secondOption: Option[T] = list.drop(1).headOption
def zipWithIndex: List[(T, Int)] = list.zipWithIndex
}
// 使用
"racecar".isPalindrome // true
"Hello World".wordCount // 2
List(1, 2, 3).secondOption // Some(2)
四、语法层面的现代化改进
4.1 枚举类型(Enum)
Scala 3 引入了原生枚举语法,大大减少了定义代数数据类型(ADT)的样板代码:
// Scala 3 Enum - 简洁的 ADT 定义
enum Color {
case Red, Green, Blue
}
// 带参数的枚举
enum Planet(val mass: Double, val radius: Double) {
case Mercury extends Planet(3.303e+23, 2.4397e6)
case Venus extends Planet(4.869e+24, 6.0518e6)
case Earth extends Planet(5.976e+24, 6.37814e6)
def surfaceGravity: Double = 6.67300E-11 * mass / (radius * radius)
}
// 枚举可以包含方法和字段
enum Direction(val dx: Int, val dy: Int) {
case Up extends Direction(0, 1)
case Down extends Direction(0, -1)
case Left extends Direction(-1, 0)
case Right extends Direction(1, 0)
def opposite: Direction = this match {
case Up => Down
case Down => Up
case Left => Right
case Right => Left
}
}
对比 Scala 2 中实现相同功能需要定义 sealed trait 和多个 case class,代码量减少了约 60%。
4.2 基于缩进的语法
Scala 3 支持使用缩进来替代花括号,使代码更加简洁:
// Scala 3 indentation-based syntax
def factorial(n: Int): BigInt =
if n <= 1 then 1
else n * factorial(n - 1)
// 类定义也可以使用缩进
class User(name: String, age: Int):
def greet: String = s"你好,我是 $name"
def isAdult: Boolean = age >= 18
// match 表达式
def describe(x: Int): String = x match
case 0 => "零"
case n if n > 0 => "正数"
case _ => "负数"
// if 表达式使用 then
val result =
if x > 0 then "positive"
else if x < 0 then "negative"
else "zero"
五、从 Scala 2 迁移的实用指南
5.1 迁移策略
Scala 3 提供了完善的迁移工具链,推荐采用渐进式迁移策略。Scala 2.13 可以直接使用 Scala 3 编写的库(通过 TASTy 读取器),因此项目可以逐模块迁移。
迁移的基本步骤如下:
- 升级到 Scala 2.13.6+:使用
-Xsource:3编译器标志,提前适配 Scala 3 语法 - 消除弃用警告:修复所有
-Xsource:3产生的编译警告 - 替换隐式转换:将
implicit def/class迁移为given和extension - 切换到 Scala 3:在 build.sbt 中更改 Scala 版本
- 运行 Scalafix:使用自动化工具完成剩余迁移
5.2 SBT 配置变更
// build.sbt 迁移配置
// Scala 2 版本
// scalaVersion := "2.13.12"
// Scala 3 版本
scalaVersion := "3.4.2"
// 关键:交叉编译设置
crossScalaVersions := Seq("2.13.12", "3.4.2")
// Scala 3 编译器选项
scalacOptions ++= Seq(
"-deprecation",
"-feature",
"-unchecked",
"-Xmax-inlines", "64" // 内联深度限制
)
// 依赖库版本需要对应 Scala 3
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "2.12.0",
"com.typesafe.akka" %% "akka-actor-typed" % "2.9.0",
"org.scalatest" %% "scalatest" % "3.2.18" % Test
)
5.3 常见迁移问题与解决方案
| Scala 2 写法 | Scala 3 替代方案 | 说明 |
|---|---|---|
implicit val x |
given x |
隐式值定义 |
implicit def f |
extension 或 given Conversion |
隐式转换 |
implicitly[T] |
summon[T] |
获取隐式实例 |
sealed trait + case class |
enum |
代数数据类型 |
implicit class |
extension |
扩展方法 |
type A with B |
A & B |
交叉类型 |
Either[A, B] |
A | B |
联合类型 |
new Trait { ... } |
new Trait with { ... } |
匿名类实例化 |
5.4 Scalafix 自动迁移
Scalafix 提供了自动化迁移规则,可以处理大部分机械性的代码变更:
# 安装 scalafix
cs install scalafix
# 运行 Scala 3 迁移规则
scalafix --rules=scala3
# 常用规则列表
# - ExplicitResultTypes: 添加显式返回类型
# - DisableSyntax: 禁用特定语法
# - LeakingImplicitClassVal: 修复隐式类泄漏
# - Scala3ImportRewrite: 重写导入语句
六、Scala 3 的性能与生态现状
截至 2026 年,Scala 3 生态系统已经相当成熟。主要框架和库都已支持 Scala 3:
- Akka / Pekko:Apache Pekko(Akka 的开源分支)完整支持 Scala 3
- Cats / Cats Effect:函数式编程核心库全面适配
- http4s / ZIO:函数式 HTTP 框架原生支持
- Play Framework:已发布 Scala 3 版本
- Spark:Apache Spark 4.0 开始实验性支持 Scala 3
在性能方面,Scala 3 编译器引入了增量编译优化和并行类型检查,大型项目的编译速度相比 Scala 2 提升了约 20-30%。生成的字节码质量也有所改善,特别是在模式匹配优化和内联(Inline)方面。
如果你的团队还在使用 Scala 2,现在正是迁移的好时机。Scala 3 的语言特性让代码更简洁、类型更安全、维护成本更低。按照本文的迁移指南,你可以逐步、安全地完成升级。
汤不热吧