一、问题导入:从if-else到模式匹配的优雅转型
在日常开发中,我们经常需要根据一个值来查找对应的另一个值。比如根据身份证号码的前两位判断籍贯:
- 42 表示湖北
- 33 表示浙江
- 11 表示北京
- 31 表示上海
传统if-else的繁琐写法
案例:使用if-else实现省份代码匹配
package matchcase
object case01 {
def main(args: Array[String]): Unit = {
val code = "11"
var province = ""
if (code == "42") {
province = "湖北"
} else if (code == "11") {
province = "北京"
} else {
province = "未知"
}
println(s"${code} 对应的省份是:${province}")
}
}
运行结果:
代码分析:
- 使用多个if-else语句判断code的值,每个分支都需要写完整的条件表达式
- 变量province需要先声明再赋值,增加了代码行数
- 当分支增多时,代码可读性下降,维护困难
模式匹配的简洁方案
案例:使用match-case重构省份匹配
package matchcase
object case02 {
def main(args: Array[String]): Unit = {
val code = "11"
val province = code match {
case "42" => "湖北"
case "11" => "北京"
case _ => "未知"
}
println(s"${code} 对应的省份是:${province}")
}
}
运行结果:
代码分析:
- 使用match-case替代if-else,代码量减少约40%
case _作为默认分支,优雅处理所有未匹配的情况- match-case是表达式而非语句,可以直接赋值给变量
- 语法更加直观,类似自然语言描述
二、match-case基础语法详解
2.1 核心语法结构
value match {
case pattern1 => result1
case pattern2 => result2
...
case patternN => resultN
case _ => 其他
}
执行机制:
- 从左到右依次尝试匹配每个case模式
- 第一个匹配成功的case分支会被执行
- 如果没有匹配成功且存在
case _,则执行默认分支 - 如果没有匹配成功且没有默认分支,抛出
MatchError异常
2.2 数值匹配示例
案例:数字转英文单词
package matchcase
object case03 {
def main(args: Array[String]): Unit = {
// 输入一个1-5的数字,打印它对应的英语单词
val num = 10
val word = num match {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case 4 => "four"
case 5 => "five"
case _ => "other"
}
println(s"${num} 对应的英文是:${word}")
}
}
运行结果:
代码分析:
- 变量
num的值为10,与case 1-5都不匹配 - 最终匹配到
case _默认分支,返回 "other" - 整个match-case结构作为表达式返回结果并赋值给word变量
2.3 重要注意事项
错误示例分析:
// 错误:case _ 放在前面
num match {
case _ => "other" // 这个分支会匹配所有情况
case 1 => "one" // 永远不会被执行到
case 2 => "two" // 永远不会被执行到
}
// 错误:省略 case _
num match {
case 1 => "one"
case 2 => "two"
}
// 如果num=3,会抛出MatchError: 3
三、高阶匹配之元组与数组匹配
3.1 元组元素数量匹配
案例:根据元组元素数量进行不同处理
package matchcase
object case04 {
def main(args: Array[String]): Unit = {
// 元组匹配
val t1 = (2, 3)
t1 match {
case (a, b) => println(s"有两个元素 ${a}, ${b}")
case _ => println("未知")
}
// 数组匹配
val arr1 = Array(10, 2)
arr1 match {
case Array(1, x, y) => println("数组,第一个元素是1,长度为3")
case Array(10, x) => println("数组,第一个元素是10,长度为2")
case _ => println("其他")
}
}
}
运行结果:
代码分析:
元组匹配部分:
- 元组
(2, 3)有两个元素,匹配case (a, b) - 模式中的a和b是变量,分别绑定到元组的两个元素值
- 这种匹配不仅检查元素数量,还解构元组提取值
数组匹配部分:
- 数组
Array(10, 2)长度为2,第一个元素为10 - 匹配
case Array(10, x),其中10是字面量,x是变量 - x绑定到数组的第二个元素值2
- 这种匹配结合了元素值检查和变量绑定
3.2 数组特征匹配的高级用法
// 匹配特定模式的数组
arr match {
case Array(0) => // 长度为1,元素为0
case Array(1, _, _) => // 长度为3,第一个元素为1
case Array(1, _*) => // 长度任意,第一个元素为1
case Array(0, tail @ _*) => // 长度任意,第一个元素为0,tail绑定剩余元素
}
四、类型匹配与变量声明中的模式匹配
4.1 变量类型匹配
案例:根据变量类型执行不同操作
package matchcase
object case05 {
def matchType(value: Any): Unit = {
value match {
case x: Int => println(s"${x} 是Int")
case x: Double => println(s"${x} 是Double")
case _ => println("未知")
}
}
def main(args: Array[String]): Unit = {
matchType(1)
matchType(1.1)
}
}
运行结果:
代码分析:
- 函数
matchType接受Any类型参数,可以接收任意类型的值 case x: Int匹配整型值,并将值绑定到变量xcase x: Double匹配双精度浮点型值- 类型匹配在运行时进行,基于值的实际类型而非声明类型
- 这种匹配方式在处理异构集合时特别有用
4.2 变量声明中的模式匹配
案例:使用模式匹配提取数组元素
package matchcase
object case08 {
def main(args: Array[String]): Unit = {
// 定义一个数组,其中有4个元素
val arr = Array(11, 22, 33, 4)
// 传统方式:逐个获取数组元素
// var x = arr(0)
// var y = arr(1)
// var z = arr(2)
// 模式匹配方式:一次性提取多个元素
val Array(x, y, z, _*) = arr
println("x=" + x + ",y=" + y + ",z=" + z)
}
}
运行结果:
代码分析:
val Array(x, y, z, _*) = arr是变量声明中的模式匹配- 将数组arr的前三个元素分别绑定到变量x、y、z
_*匹配剩余的所有元素,表示我们不关心剩余元素- 这种方式比传统索引访问更简洁,特别是需要提取多个元素时
- 编译时会检查模式是否可能匹配失败,增加安全性
五、模式匹配的核心优势总结
| 特性 | if-else语句 | match-case表达式 |
|---|---|---|
| 返回值 | 无返回值(语句) | 有返回值(表达式) |
| 模式支持 | 只能进行条件判断 | 支持多种模式(值、类型、结构) |
| 默认处理 | 需要显式写else | 使用case _作为默认分支 |
| 代码简洁性 | 分支多时代码冗长 | 语法紧凑,可读性好 |
| 编译检查 | 有限 | 编译器会检查是否覆盖所有情况 |