Scala 模式匹配:match 表达式与 case 类
模式匹配是 Scala 语言最强大的特性之一,它类似于 Java 中的 switch 语句,但功能更为丰富。它不仅可以匹配值,还可以匹配类型、结构和表达式。通过 match 表达式与 case 类的结合,可以极大地简化代码逻辑。
一、 基础 match 表达式
match 表达式由 match 关键字和多个 case 块组成。每个 case 块定义了一个模式,如果匹配成功,则执行该块对应的逻辑。
- 打开 REPL 终端或 IDE。
- 定义 一个变量。
- 输入 以下代码,使用
match关键字进行匹配:
val number = 3
val description = number match {
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "其他"
}
- 观察 输出结果。
number的值为3,程序依次对比case 1和case 2,均不匹配。- 当对比到
case 3时匹配成功,返回 字符串"三"。 case _是通配符,类似于 Java 中的default,用于匹配所有未被前面条件捕获的情况。
二、 模式匹配中的守卫
如果需要在匹配时增加额外的条件判断,可以使用守卫。守卫通过 if 关键字在 case 语句后添加布尔表达式。
- 编写 包含守卫的匹配表达式:
val score = 85
val grade = score match {
case s if s >= 90 => "优秀"
case s if s >= 60 => "及格"
case _ => "不及格"
}
- 执行 代码。
- 变量
score的值为85。 - 第一个
case尝试匹配,判断85 >= 90为false,跳过。 - 第二个
case尝试匹配,判断85 >= 60为true,赋值s为85,返回"及格"。
- 变量
三、 Case 类的定义与使用
case 类是一种特殊的类,它们默认是不可变的,并且自动实现了 apply、hashCode、equals 和 toString 等方法,非常适合用于模式匹配。
- 定义 一个
case类结构:
case class Person(name: String, age: Int)
- 创建 实例。
- 不需要使用
new关键字,直接 调用 类名即可:
- 不需要使用
val alice = Person("Alice", 25)
四、 匹配 Case 类结构
模式匹配的真正威力在于解构复杂对象。可以直接在 case 语句中提取 case 类内部的字段。
- 编写 针对
case类的匹配逻辑:
val user = Person("Bob", 15)
val greeting = user match {
case Person("Alice", _) => "你好,Alice"
case Person(name, age) if age < 18 => s"你好,未成年用户 $name"
case Person(name, _) => s"你好,$name"
}
- 分析 匹配过程:
user是Person("Bob", 15)。- 第一个
case要求名字必须是"Alice",匹配失败。 - 第二个
case提取名字为name,年龄为age,并检查age < 18。此时age为15,条件成立,返回"你好,未成年用户 Bob"。
五、 匹配类型与嵌套结构
除了匹配具体的值和对象结构,还可以匹配变量的运行时类型,以及嵌套的对象结构。
- 定义 一个通用的匹配方法,处理不同类型的数据:
def process(input: Any): String = input match {
case i: Int => s"收到整数: $i"
case s: String => s"收到字符串: $s"
case Person(name, age) => s"收到 Person 对象: $name"
case _ => "未知类型"
}
```
2. **测试** 不同的输入:
```scala
process(10) // 输出: 收到整数: 10
process("hello") // 输出: 收到字符串: hello
process(Person("Tom", 30)) // 输出: 收到 Person 对象: Tom
```
3. **处理** 嵌套结构。
* 假设有一个 `case` 类包含另一个 `case` 类:
```scala
case class Student(school: String, person: Person)
val complexData = Student("一中", Person("Mike", 16))
val result = complexData match {
case Student(school, Person(name, _)) => s"$name 就读于 $school"
}
```
4. **执行** 上述代码,程序将直接 **提取** 出嵌套在 `Student` 内部的 `Person` 对象的 `name` 字段,**输出** `"Mike 就读于 一中"`。
---
### 六、 密封类
为了保证模式匹配的完备性(即覆盖所有可能的情况),通常将父类标记为 `sealed`(密封的)。编译器会检查是否所有子类都已匹配,若未覆盖全,编译器会发出警告。
1. **定义** 一个密封特质(Trait):
```scala
sealed trait Message
case class Text(content: String) extends Message
case class Image(url: String) extends Message
case class Video(duration: Int) extends Message
```
2. **编写** 处理消息的函数:
```scala
def handleMessage(msg: Message): Unit = msg match {
case Text(content) => println(s"文本: $content")
case Image(url) => println(s"图片链接: $url")
// 如果这里注释掉 Video 的分支,编译器会报出警告
case Video(duration) => println(s"视频时长: $duration")
}
- 确认 编译状态。
- 如果 遗漏 了
Video分支的匹配,编译器会提示match may not be exhaustive,这有助于避免运行时错误。
- 如果 遗漏 了
七、 实战案例:简单的通知系统
结合上述知识点,构建一个简单的通知系统,处理不同类型的消息。
- 定义 基础数据结构:
sealed trait Notification
case class Email(sender: String, title: String) extends Notification
case class SMS(caller: String, msg: String) extends Notification
case class PushAlert(level: Int, content: String) extends Notification
- 编写 处理逻辑函数:
def processNotification(note: Notification): String = note match {
case Email(sender, title) if sender == "boss" =>
s"紧急邮件来自 $sender: $title"
case Email(sender, title) =>
s"普通邮件: $title (来自 $sender)"
case SMS("10086", msg) =>
s"系统通知短信: $msg"
case SMS(caller, msg) =>
s"短信来自 $caller: $msg"
case PushAlert(level, content) if level >= 3 =>
s"高优先级提醒: $content"
case PushAlert(_, content) =>
s"普通提醒: $content"
}
- 调用 函数并验证结果:
val email = Email("boss", "会议通知")
val sms = SMS("10086", "余额不足")
val alert = PushAlert(5, "系统异常")
println(processNotification(email)) // 输出: 紧急邮件来自 boss: 会议通知
println(processNotification(sms)) // 输出: 系统通知短信: 余额不足
println(processNotification(alert)) // 输出: 高优先级提醒: 系统异常
暂无评论,快来抢沙发吧!