Scala模式匹配(上篇):从基础语法到类型匹配

38 阅读5分钟

一、问题导入:从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}")
  }
}

运行结果

11.png

代码分析

  1. 使用多个if-else语句判断code的值,每个分支都需要写完整的条件表达式
  2. 变量province需要先声明再赋值,增加了代码行数
  3. 当分支增多时,代码可读性下降,维护困难

模式匹配的简洁方案

案例:使用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}")
  }
}

运行结果22.png

代码分析

  1. 使用match-case替代if-else,代码量减少约40%
  2. case _ 作为默认分支,优雅处理所有未匹配的情况
  3. match-case是表达式而非语句,可以直接赋值给变量
  4. 语法更加直观,类似自然语言描述

二、match-case基础语法详解

2.1 核心语法结构

value match {
    case pattern1 => result1
    case pattern2 => result2
    ...
    case patternN => resultN
    case _ => 其他
}

执行机制

  1. 从左到右依次尝试匹配每个case模式
  2. 第一个匹配成功的case分支会被执行
  3. 如果没有匹配成功且存在 case _,则执行默认分支
  4. 如果没有匹配成功且没有默认分支,抛出 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}")
  }
}

运行结果33.png

代码分析

  1. 变量 num 的值为10,与case 1-5都不匹配
  2. 最终匹配到 case _ 默认分支,返回 "other"
  3. 整个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("其他")
    }
  }
}

运行结果44.png

代码分析

元组匹配部分

  1. 元组 (2, 3) 有两个元素,匹配 case (a, b)
  2. 模式中的a和b是变量,分别绑定到元组的两个元素值
  3. 这种匹配不仅检查元素数量,还解构元组提取值

数组匹配部分

  1. 数组 Array(10, 2) 长度为2,第一个元素为10
  2. 匹配 case Array(10, x),其中10是字面量,x是变量
  3. x绑定到数组的第二个元素值2
  4. 这种匹配结合了元素值检查和变量绑定

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)
  }
}

运行结果

55.png

代码分析

  1. 函数 matchType 接受 Any 类型参数,可以接收任意类型的值
  2. case x: Int 匹配整型值,并将值绑定到变量x
  3. case x: Double 匹配双精度浮点型值
  4. 类型匹配在运行时进行,基于值的实际类型而非声明类型
  5. 这种匹配方式在处理异构集合时特别有用

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)
  }
}

运行结果

88.png 代码分析

  1. val Array(x, y, z, _*) = arr 是变量声明中的模式匹配
  2. 将数组arr的前三个元素分别绑定到变量x、y、z
  3. _* 匹配剩余的所有元素,表示我们不关心剩余元素
  4. 这种方式比传统索引访问更简洁,特别是需要提取多个元素时
  5. 编译时会检查模式是否可能匹配失败,增加安全性

五、模式匹配的核心优势总结

特性if-else语句match-case表达式
返回值无返回值(语句)有返回值(表达式)
模式支持只能进行条件判断支持多种模式(值、类型、结构)
默认处理需要显式写else使用case _作为默认分支
代码简洁性分支多时代码冗长语法紧凑,可读性好
编译检查有限编译器会检查是否覆盖所有情况