scala模式匹配
Scala 模式匹配:超越 switch 的强大匹配机制
模式匹配是 Scala 中最具特色的功能之一,它不仅能替代 Java 中的 switch...case,还支持类型匹配、集合匹配、对象匹配等复杂场景,甚至可以结合条件判断实现灵活的逻辑分支。本文将全面解析 Scala 模式匹配的语法、特性及应用场景。
基本语法:match 与 case
Scala 模式匹配通过 match 关键字定义匹配主体,case 关键字定义分支,语法简洁且功能强大。
基本用法(类似 switch)
val operator: Char = '*'
val num1: Int = 10
val num2: Int = 2
// 匹配运算符,执行对应计算
val result = operator match {
case '+' => num1 + num2
case '-' => num1 - num2
case '*' => num1 * num2
case '/' => num1 / num2
case _ => throw new Exception("不支持的运算符") // 默认分支,类似 default
}
println(result) // 输出:20
核心特点:
- 每个
case分支通过=>连接匹配条件和执行逻辑。 - 匹配成功后自动退出(无需
break),效率高于 Java 的switch。 case _表示默认分支,匹配所有未被其他分支覆盖的情况。
与 Java switch 的区别
| 特性 | Scala 模式匹配 | Java switch |
|---|---|---|
| 匹配类型 | 支持值、类型、集合、对象等 | 仅支持基本类型、枚举、字符串(Java 7+) |
| 分支执行 | 自动退出(无需 break) | 需要显式 break,否则穿透 |
| 返回值 | 整个 match 表达式有返回值 | 无返回值(需手动赋值) |
| 默认分支 | case _ |
default |
类型匹配:根据数据类型分支
Scala 模式匹配可直接根据变量的运行时类型进行分支,无需显式调用 isInstanceOf。
示例:匹配不同类型
val elements: List[Any] = List(10, "hello", 3.14, true, 'a')
for (elem <- elements) {
elem match {
case i: Int => println(s"整数:$i")
case s: String => println(s"字符串:$s")
case d: Double => println(s"双精度浮点数:$d")
case b: Boolean => println(s"布尔值:$b")
case _ => println(s"未知类型:${elem.getClass.getSimpleName}")
}
}
输出结果:
整数:10
字符串:hello
双精度浮点数:3.14
布尔值:true
未知类型:Character
注意:
- 类型匹配中,变量名(如
i、s)用于接收匹配到的值,若无需使用可简化为_(如case _: Int)。 - 泛型类型匹配时,由于类型擦除,
case list: List[Int]无法区分List[Int]和List[String],需结合其他条件。
条件守卫:匹配后的二次判断
当基本匹配无法满足需求时,可在 case 后添加 if 条件(模式守卫),实现更精细的筛选。
示例:带条件的数值匹配
def judgeNumber(n: Int): String = n match {
case x if x > 0 => s"$x 是正数"
case x if x < 0 => s"$x 是负数"
case 0 => "零"
}
println(judgeNumber(5)) // 输出:5 是正数
println(judgeNumber(-3)) // 输出:-3 是负数
println(judgeNumber(0)) // 输出:零
示例:结合类型匹配的守卫
val data: List[Any] = List(15, "scala", 8.9, -2)
for (d <- data) {
d match {
case i: Int if i > 10 => println(s"大于10的整数:$i")
case s: String if s.length > 3 => println(s"长度大于3的字符串:$s")
case _ => println(s"未匹配:$d")
}
}
输出结果:
大于10的整数:15
长度大于3的字符串:scala
未匹配:8.9
未匹配:-2
变量绑定:在匹配中提取值
case 后可直接声明变量,匹配成功后将值绑定到该变量,便于后续处理。
示例:提取字符并处理
val char: Char = 'a'
char match {
case '+' => println("匹配到加号")
case '-' => println("匹配到减号")
case ch => println(s"其他字符:$ch(ASCII码:${ch.toInt})") // 绑定到变量 ch
}
// 输出:其他字符:a(ASCII码:97)
示例:结合类型的变量绑定
val obj: Any = "hello scala"
obj match {
case s: String => println(s"字符串内容:$s,长度:${s.length}") // s 绑定字符串值
case i: Int => println(s"整数:$i")
}
// 输出:字符串内容:hello scala,长度:11
集合匹配:数组、列表与元组
Scala 模式匹配可直接匹配集合的结构和元素,如数组的长度、列表的头部元素等。
数组匹配
val arrays: List[Array[Int]] = List(
Array(0),
Array(1, 2),
Array(0, 1, 2),
Array(3, 4, 5)
)
for (arr <- arrays) {
arr match {
case Array(0) => println("仅包含 0 的数组")
case Array(x, y) => println(s"两个元素的数组:$x, $y")
case Array(0, _*) => println("以 0 开头的数组") // _* 匹配任意数量的元素
case _ => println("其他数组")
}
}
输出结果:
仅包含 0 的数组
两个元素的数组:1, 2
以 0 开头的数组
其他数组
列表匹配
列表匹配类似数组,可通过 :: 操作符匹配头部和尾部。
val lists: List[List[Int]] = List(
List(0),
List(1, 2),
List(0, 1, 2),
List(3, 4, 5)
)
for (list <- lists) {
list match {
case 0 :: Nil => println("仅包含 0 的列表") // Nil 表示空列表
case x :: y :: Nil => println(s"两个元素的列表:$x, $y")
case 0 :: tail => println(s"以 0 开头的列表,尾部:$tail") // tail 匹配剩余元素
case _ => println("其他列表")
}
}
输出结果:
仅包含 0 的列表
两个元素的列表:1, 2
以 0 开头的列表,尾部:List(1, 2)
其他列表
元组匹配
元组匹配可精确匹配元素数量和值:
val tuples: List[(Int, String)] = List(
(0, "zero"),
(1, "one"),
(2, "two")
)
for (t <- tuples) {
t match {
case (0, s) => println(s"键为 0,值为:$s")
case (k, "one") => println(s"值为 one,键为:$k")
case (k, v) => println(s"键:$k,值:$v")
}
}
输出结果:
键为 0,值为:zero
值为 one,键为:1
键:2,值:two
对象匹配:自定义类的匹配
要匹配自定义对象,需在其伴生对象中定义 unapply 方法(提取器),用于从对象中提取值供匹配。
步骤:实现对象匹配
- 定义类及构造参数。
- 在伴生对象中实现
unapply方法,返回Option类型(提取成功返回Some(值),失败返回None)。
示例:学生对象匹配
// 定义学生类
class Student(val id: Int, val name: String)
// 伴生对象(包含提取器 unapply)
object Student {
// 提取器:从 Student 对象中提取 id
def unapply(stu: Student): Option[Int] = Some(stu.id)
}
// 测试匹配
val students: List[Student] = List(
new Student(1, "Alice"),
new Student(2, "Bob"),
new Student(1, "Charlie")
)
for (stu <- students) {
stu match {
case Student(1) => println(s"ID为1的学生:${stu.name}") // 调用 unapply 提取 id 并匹配
case Student(id) => println(s"其他ID的学生:ID=$id,姓名=${stu.name}")
}
}
输出结果:
ID为1的学生:Alice
其他ID的学生:ID=2,姓名=Bob
ID为1的学生:Charlie
扩展:提取多个值(返回元组的 Option):
object Student {
// 提取 id 和 name
def unapply(stu: Student): Option[(Int, String)] = Some((stu.id, stu.name))
}
// 匹配时提取多个值
stu match {
case Student(1, name) => println(s"ID=1,姓名=$name")
case Student(id, name) => println(s"ID=$id,姓名=$name")
}
模式匹配的最佳实践
- 优先使用模式匹配替代 if-else:对于多分支判断,模式匹配更简洁易读。
- 利用变量绑定简化代码:无需显式转换类型,直接通过变量使用匹配到的值。
- 结合集合结构匹配:快速筛选特定结构的数组、列表或元组。
- 自定义提取器实现对象匹配:使自定义类支持模式匹配,提升代码优雅性。
- 避免过度复杂的守卫条件:复杂逻辑可封装为方法,在守卫中调用。