Scala 的模式匹配是一种强大的多分支条件判断机制,比 Java 的 switch 更灵活、更具表达力,可匹配值、类型、结构、样例类等,甚至支持守卫条件和偏函数。本文从基础到进阶全面讲解 Scala 模式匹配的核心用法。
一、基础语法
模式匹配通过 match 关键字实现,语法结构:
scala
变量/表达式 match {
case 模式1 => 逻辑1 // 匹配成功执行对应逻辑
case 模式2 => 逻辑2
case _ => 默认逻辑 // 通配符,匹配所有未命中的情况(类似 switch 的 default)
}
核心特点:
- 匹配是顺序执行的,命中第一个匹配的
case后立即返回,不会穿透(无需break); - 若未匹配到任何
case且无_,会抛出MatchError; - 匹配结果可直接赋值给变量(表达式特性)。
二、常见匹配场景
1. 值匹配(基础)
匹配固定值(数字、字符串、布尔值等),类似增强版 switch:
scala
def matchValue(x: Any): String = x match {
case 1 => "数字 1"
case "scala" => "字符串 scala"
case true => "布尔值 true"
case _ => "未匹配到" // 通配符,兜底
}
println(matchValue(1)) // 输出:数字 1
println(matchValue("java")) // 输出:未匹配到
2. 类型匹配
匹配变量的实际类型(而非声明类型),语法:case 变量: 类型:
scala
def matchType(x: Any): String = x match {
case s: String => s"字符串:$s"
case i: Int => s"整数:$i"
case l: List[_] => s"列表:$l" // 通配符 _ 表示任意泛型的 List
case _: Double => "浮点数(忽略值)"
case _ => "未知类型"
}
println(matchType(List(1,2,3))) // 输出:列表:List(1, 2, 3)
println(matchType(3.14)) // 输出:浮点数(忽略值)
⚠️ 注意:泛型擦除问题 ——Scala 在运行时会擦除泛型类型,因此 case l: List[Int] 无法精准匹配 List[Int],需结合其他方式(如守卫)。
3. 守卫条件(Guard)
在 case 后加 if 条件,实现更精细的匹配:
scala
def matchGuard(x: Int): String = x match {
case i if i > 0 => s"正数:$i"
case i if i < 0 => s"负数:$i"
case 0 => "零"
case _ => "非整数"
}
println(matchGuard(10)) // 输出:正数:10
println(matchGuard(-5)) // 输出:负数:-5
4. 集合匹配(List/Array/Tuple)
匹配集合的结构(长度、元素值、通配符):
(1)List 匹配
scala
def matchList(list: List[Int]): String = list match {
case Nil => "空列表"
case List(1, 2, 3) => "固定元素列表 [1,2,3]"
case 1 :: Nil => "单元素列表 [1]" // :: 是列表构造符
case head :: tail => s"非空列表,头:$head,尾:$tail" // 匹配任意非空列表
case _ => "其他列表"
}
println(matchList(List(1,2,3))) // 输出:固定元素列表 [1,2,3]
println(matchList(List(5,6,7))) // 输出:非空列表,头:5,尾:List(6, 7)
(2)Tuple 匹配
scala
def matchTuple(tuple: Any): String = tuple match {
case (1, x) => s"二元组,第一个元素为 1,第二个:$x"
case (x, y, z) => s"三元组:($x, $y, $z)"
case _ => "非目标元组"
}
println(matchTuple((1, "scala"))) // 输出:二元组,第一个元素为 1,第二个:scala
println(matchTuple((2, 3, 4))) // 输出:三元组:(2, 3, 4)
(3)Array 匹配
scala
def matchArray(arr: Array[Int]): String = arr match {
case Array(1) => "单元素数组 [1]"
case Array(1, _*) => "以 1 开头的数组" // _* 匹配任意数量的后续元素
case Array(x, y) => s"二元数组:[$x, $y]"
case _ => "其他数组"
}
println(matchArray(Array(1,2,3))) // 输出:以 1 开头的数组
println(matchArray(Array(5,6))) // 输出:二元数组:[5, 6]
5. 样例类(Case Class)匹配
样例类是 Scala 专为模式匹配设计的类,匹配时可直接提取属性,是面向对象 + 模式匹配的核心用法:
scala
// 定义样例类
case class Person(name: String, age: Int)
case class Student(name: String, grade: Int)
case object Empty // 样例对象(无属性)
def matchCaseClass(obj: Any): String = obj match {
case Person(name, age) if age > 18 => s"成年人:$name,年龄:$age"
case Person(name, age) => s"未成年人:$name,年龄:$age"
case Student(name, 9) => s"九年级学生:$name"
case Empty => "空对象"
case _ => "非目标类型"
}
println(matchCaseClass(Person("张三", 20))) // 输出:成年人:张三,年龄:20
println(matchCaseClass(Student("李四", 9))) // 输出:九年级学生:李四
println(matchCaseClass(Empty)) // 输出:空对象
6. 变量绑定(@ 符号)
匹配模式的同时,将整个匹配结果绑定到变量,方便后续使用:
scala
def matchBind(list: List[Int]): String = list match {
case l @ List(1, _*) => s"匹配到列表:$l(以 1 开头)" // l 绑定整个列表
case Person(name, _) @ Person(_, 25) => s"年龄 25 的人:$name,对象:$Person"
case _ => "未绑定"
}
println(matchBind(List(1,2,3))) // 输出:匹配到列表:List(1, 2, 3)(以 1 开头)
7. 偏函数中的模式匹配
偏函数(PartialFunction)是只对部分输入值有定义的函数,核心是 isDefinedAt + apply,语法上可通过 case 直接定义:
scala
// 定义偏函数:仅处理 Int 类型且值 > 0 的输入
val positive: PartialFunction[Any, String] = {
case i: Int if i > 0 => s"正数:$i"
}
// 使用偏函数
println(positive.isDefinedAt(10)) // true
println(positive.isDefinedAt(-5)) // false
println(positive(10)) // 输出:正数:10
// 集合中使用偏函数(collect 方法)
val list = List(1, -2, "scala", 3.14, 5)
val result = list.collect(positive)
println(result) // 输出:List(正数:1, 正数:5)
三、高级特性
1. 密封类(Sealed Class)
密封类限制子类必须定义在同一文件中,确保模式匹配的穷尽性(编译器可检查是否遗漏 case):
scala
sealed trait Shape // 密封特质
case class Circle(r: Double) extends Shape
case class Square(side: Double) extends Shape
def matchShape(shape: Shape): Double = shape match {
case Circle(r) => Math.PI * r * r // 圆面积
case Square(s) => s * s // 正方形面积
// 无 _ 也不会报错,因为编译器知道 Shape 只有两个子类
}
println(matchShape(Circle(2))) // 输出:12.566370614359172
2. 匹配中赋值
模式匹配的结果可直接赋值给变量,简化代码:
scala
val (x, y) = (1, 2) // 元组解构赋值
println(x) // 1
val List(a, b, _) = List(3,4,5) // 列表解构赋值
println(b) // 4
val Person(name, age) = Person("王五", 30) // 样例类解构赋值
println(name) // 王五
四、常见注意事项
-
匹配顺序:优先匹配具体的
case,再匹配通用的(如_),避免前面的case覆盖后面的; -
穷尽性检查:使用密封类时,编译器会检查是否覆盖所有子类,非密封类建议加
_兜底; -
泛型擦除:运行时无法区分
List[Int]和List[String],需结合类型标签(TypeTag)或守卫; -
样例类的 copy 方法:匹配后可通过
copy方法修改属性(样例类默认实现):scala
val p = Person("赵六", 25) val p2 = p match { case Person(n, a) => p.copy(age = a + 1) } println(p2) // Person(赵六,26)
五、总结
Scala 模式匹配是其核心特性之一,核心优势:
- 支持多维度匹配(值、类型、结构、对象);
- 表达式特性,可直接赋值;
- 结合样例类 / 密封类,实现类型安全的分支逻辑;
- 偏函数进一步简化 “部分匹配” 场景。
相比 Java 的 switch,Scala 模式匹配更灵活、更贴近业务逻辑,是处理多分支判断的首选方式。