0%

scala模式匹配

Scala 模式匹配:超越 switch 的强大匹配机制

模式匹配是 Scala 中最具特色的功能之一,它不仅能替代 Java 中的 switch...case,还支持类型匹配、集合匹配、对象匹配等复杂场景,甚至可以结合条件判断实现灵活的逻辑分支。本文将全面解析 Scala 模式匹配的语法、特性及应用场景。

基本语法:match 与 case

Scala 模式匹配通过 match 关键字定义匹配主体,case 关键字定义分支,语法简洁且功能强大。

基本用法(类似 switch)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

示例:匹配不同类型

1
2
3
4
5
6
7
8
9
10
11
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}")
}
}

输出结果

1
2
3
4
5
整数:10
字符串:hello
双精度浮点数:3.14
布尔值:true
未知类型:Character

注意

  • 类型匹配中,变量名(如 is)用于接收匹配到的值,若无需使用可简化为 _(如 case _: Int)。
  • 泛型类型匹配时,由于类型擦除,case list: List[Int] 无法区分 List[Int]List[String],需结合其他条件。

条件守卫:匹配后的二次判断

当基本匹配无法满足需求时,可在 case 后添加 if 条件(模式守卫),实现更精细的筛选。

示例:带条件的数值匹配

1
2
3
4
5
6
7
8
9
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)) // 输出:零

示例:结合类型匹配的守卫

1
2
3
4
5
6
7
8
9
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")
}
}

输出结果

1
2
3
4
大于10的整数:15
长度大于3的字符串:scala
未匹配:8.9
未匹配:-2

变量绑定:在匹配中提取值

case 后可直接声明变量,匹配成功后将值绑定到该变量,便于后续处理。

示例:提取字符并处理

1
2
3
4
5
6
7
8
val char: Char = 'a'

char match {
case '+' => println("匹配到加号")
case '-' => println("匹配到减号")
case ch => println(s"其他字符:$ch(ASCII码:${ch.toInt})") // 绑定到变量 ch
}
// 输出:其他字符:a(ASCII码:97)

示例:结合类型的变量绑定

1
2
3
4
5
6
7
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 模式匹配可直接匹配集合的结构和元素,如数组的长度、列表的头部元素等。

数组匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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("其他数组")
}
}

输出结果

1
2
3
4
仅包含 0 的数组
两个元素的数组:1, 2
以 0 开头的数组
其他数组

列表匹配

列表匹配类似数组,可通过 :: 操作符匹配头部和尾部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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("其他列表")
}
}

输出结果

1
2
3
4
仅包含 0 的列表
两个元素的列表:1, 2
以 0 开头的列表,尾部:List(1, 2)
其他列表

元组匹配

元组匹配可精确匹配元素数量和值:

1
2
3
4
5
6
7
8
9
10
11
12
13
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")
}
}

输出结果

1
2
3
键为 0,值为:zero
值为 one,键为:1
键:2,值:two

对象匹配:自定义类的匹配

要匹配自定义对象,需在其伴生对象中定义 unapply 方法(提取器),用于从对象中提取值供匹配。

步骤:实现对象匹配

  1. 定义类及构造参数。
  2. 在伴生对象中实现 unapply 方法,返回 Option 类型(提取成功返回 Some(值),失败返回 None)。

示例:学生对象匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 定义学生类
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}")
}
}

输出结果

1
2
3
ID为1的学生:Alice
其他ID的学生:ID=2,姓名=Bob
ID为1的学生:Charlie

扩展:提取多个值(返回元组的 Option):

1
2
3
4
5
6
7
8
9
10
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")
}

模式匹配的最佳实践

  1. 优先使用模式匹配替代 if-else:对于多分支判断,模式匹配更简洁易读。
  2. 利用变量绑定简化代码:无需显式转换类型,直接通过变量使用匹配到的值。
  3. 结合集合结构匹配:快速筛选特定结构的数组、列表或元组。
  4. 自定义提取器实现对象匹配:使自定义类支持模式匹配,提升代码优雅性。
  5. 避免过度复杂的守卫条件:复杂逻辑可封装为方法,在守卫中调用。

欢迎关注我的其它发布渠道