一、基本语法
Scala 中的模式匹配类似于 Java 中的 switch 语法,但是功能更强大
scala
/**
[var a = / def func() = ] var_name match {
case value1 => statement1
case value2 => statement2
case value3 => statement3
case _ => defaultOP
}
*/
object TestMatchCase {
def main(args: Array[String]): Unit = {
val a = 10
val b = 20
def matchCalculate(op: Char): Any = op match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "非法运算符"
}
println(matchCalculate('+'))
println(matchCalculate('*'))
println(matchCalculate('@'))
}
}
- 如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句,若此时没有 case _ 分支,那么会抛出 MatchError
- 每个 case 中,不需要使用 break 语句,会自动中断 case
- match case 语句可以匹配任何类型,而不只是字面量
- => 后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行,可以使用 {} 括起来,也可以省略
二、模式守卫
在模式匹配中增加条件守卫可以匹配某个范围的数据
scala
object TestMatchGuard {
def main(args: Array[String]): Unit = {
// 使用模式守卫实现绝对值
def abs(num: Int): Int = {
num match {
case i: Int if i >= 0 => i
case i: Int if i < 0 => -i
}
}
println(abs(22))
println(abs(0))
println(abs(-18))
}
}
三、模式匹配类型
1. 匹配常量
模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等
scala
object TestMatchTypes {
def main(args: Array[String]): Unit = {
def describe(x: Any): String = x match {
case 1 => "Int one"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
case _ => "" // _ 是占位符,可以用其他字符替代
}
println(describe(1))
println(describe("hello"))
println(describe(0.3))
}
}
2. 匹配类型
模式匹配可以实现类似 isInstanceOf[T] 和 asInstanceOf[T] 的功能
scala
object TestMatchTypes {
def main(args: Array[String]): Unit = {
def describeType(x: Any): String = x match {
case i: Int => "Int " + i
case s: String => "String " + s
case list: List[String] => "List " + list
case array: Array[Int] => "Array[Int] " + array.mkString(",")
case a => "other type " + a // 用 _ 占位不能获取到传入的变量,可以用其他字符替代
}
println(describeType(1))
println(describeType("hello"))
println(describeType(List("hello", "scala")))
println(describeType(List(2, 3))) // 可以匹配,scala 会做泛型擦除
println(describeType(Array("hello", "scala"))) // 不能匹配,Array 没有泛型擦除
println(describeType(Array(2, 3)))
}
}
3. 匹配数组
模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为 0 的数组
scala
object TestMatchTypes {
def main(args: Array[String]): Unit = {
val arrList: List[Array[Any]] = List(
Array(0),
Array(1, 0),
Array(0, 1, 0),
Array(1, 1, 0),
Array(2, 3, 7 ,15),
Array("hello", 20, 30)
)
for(arr <- arrList) {
var result = arr match {
case Array(0) => "只有一个元素为 0 的数组"
case Array(x, y) => "有两个元素的数组:" + x + ", " + y
case Array(0, _*) => "第一个元素为 0 的数组"
case Array(x, 1, y) => "中间元素为 1 的三元素数组"
case _ => "other type"
}
println(result)
}
}
}
4. 匹配列表
4.1 方式一
scala
object TestMatchTypes {
def main(args: Array[String]): Unit = {
val lists: List[List[Any]] = List(
List(0),
List(1, 0),
List(0, 0, 0),
List(1, 1, 0),
List(1, 0, 0),
List(88),
List("hello")
)
for(list <- lists) {
var result = list match {
case List(0) => "只有一个元素为 0 的列表"
case List(x, y) => "有两个元素的列表:" + x + ", " + y
case List(0, _*) => "第一个元素为 0 的列表"
case List(x, 1, y) => "中间元素为 1 的三元素列表"
case List(a) => "只有一个元素的列表:" + a
case _ => "other type"
}
println(result)
}
}
}
4.2 方式二
scala
object TestMatchTypes {
def main(args: Array[String]): Unit = {
val list1 = List(1, 2, 3, 4, 5)
val list2 = List(5)
// first:1 second:2 rest:List(3, 4, 5)
list1 match {
case first :: second :: rest => println(s"first:$first second:$second rest:$rest") // 匹配有第一个和第二个元素的列表
case _ => println("other type")
}
// other type
list2 match {
case first :: second :: rest => println(s"first:$first second:$second rest:$rest")
case _ => println("other type")
}
}
}
5. 匹配元组
scala
object TestMatchTypes {
def main(args: Array[String]): Unit = {
val tupleList = List(
(0),
(0, 1),
(1, 1),
(0, 0, 1),
(1, 1, 0),
("hello", true, 0.5)
)
for(tuple <- tupleList) {
val result = tuple match {
case (0) => "只有一个元素 0 的元组"
case (0, _) => "第一个元素为 0 的二元组"
case (x, y) => "二元组:" + x + ", " + y
case (a, 1, _) => "中间元素为 1 的三元组:" + a
case (a, b, c) => s"三元组:$a $b $c"
case _ => "other type"
}
println(result)
}
}
}
6. 匹配对象及样例类
6.1 自定义实现
scala
// 定义一个类
class Student(val name: String, val age: Int)
// 定义伴生对象
object Student {
// 实现 apply 方法用于创建对象
def apply(name: String, age: Int): Student = new Student(name, age)
// 实现 unapply 方法用于拆解对象属性
def unapply(student: Student): Option[(String, Int)] = { // Option 防止空指针
if(student == null) {
None
} else {
Some((student.name, student.age))
}
}
}
object TestMatchObject {
def main(args: Array[String]): Unit = {
// 创建对象
val student = new Student("tom", 18)
// 对象模式匹配
val result = student match { // 对象匹配的是各属性是否相同而不是地址
// 不能使用 new 创建,且类的伴生对象必须实现 apply 和 unapply 方法
case Student("tom", 18) => "tom 18"
case _ => "other"
}
println(result)
}
}
Student("tom", 18)
在执行时,实际调用的是伴生对象中的 apply 方法构造出相应的对象- 当
Student("tom", 18)
写在 case 后时会默认调用伴生对象中的 unapply 方法(对象提取器),该对象作为 unapply 方法的参数,在 unapply 方法中将该对象的 name 和 age 属性提取出来,与new Student("tom", 18)
中的属性值进行匹配 - 只有当 case 中对象的 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功;若属性不一致,或返回 None,则匹配失败
- 若只提取对象的一个属性,则提取器为
unapply(obj: ObjClass): Option[T]
;若提取对象的多个属性,则提取器为unapply(obj: ObjClass): Option[(T1,T2,T3...)]
;若提取对象的可变个属性,则提取器为unapplySeq(obj: ObjClass): Option[Seq[T]]
6.2 样例类实现
-
样例类简介
scala/** 定义语法:case class className(field1: type1, field2: type2,...) 特点: 1.样例类仍然是类,和普通类相比,只是自动生成了伴生对象且伴生对象中自动提供了一些常用的方法,如 apply、 unapply、 toString、 equals、 hashCode 和 copy 2.样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例类可以直接使用模式匹配,而无需自己实现 unapply 方法。 3.构造器中的每一个参数默认都为 val,除非它被显式地声明为 var(不建议这样做) */ case class User(name: String, age: Int) // 无需指定 val
-
实现对象模式匹配
scala// 定义样例类 case class User(name: String, age: Int) object TestMatchObject { def main(args: Array[String]): Unit = { val user = User("bob", 20) val result = user match { case User("bob", 20) => "bob 20" case _ => "other" } println(result) } }
四、变量声明模式匹配
scala
object TestMatchVariable {
def main(args: Array[String]): Unit = {
// 使用模式匹配元组变量赋值
val (x, y, z) = (10, "hello", true)
println(s"x:$x y:$y z:$z")
// 使用模式匹配列表变量赋值
val List(first, second, _*) = List(2, 4, 7, 9)
println(s"first:$first second:$second")
val fir :: sec :: rest = List(2, 4, 7, 9)
println(s"fir:$fir sec:$sec rest:$rest") // fir:2 sec:4 rest:List(7, 9)
// 使用模式匹配对象属性赋值
val Person(name, age) = Person("zhangsan", 16)
println(s"name=$name age=$age")
}
}
五、for循环模式匹配
scala
object TestMatchFor {
def main(args: Array[String]): Unit = {
val list: List[(String, Int)] = List(("a", 1), ("b", 4), ("c",3), ("a",4))
// 1. for循环直接赋值 KV
for((k, v) <- list) { // (elem <- list)
println(k + ": " + v) // println(elem._1 + ": " + elem._2)
}
// 2. for循环只遍历 key 或 value
for((k, _) <- list) { // ((_, v) <- list)
println(k) // println(v)
}
// 3. for循环遍历指定 key 或 value
for(("a", v) <- list) { // ((k, 4) <- list)
println("a: " + v) // println(k + ": 4")
}
}
}
六、偏函数模式匹配
偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查
1. 偏函数定义
scala
/**
val funcName: PartialFunction[inParamClass, outParamClass] = {
case value => statement
}
*/
// 定义一个获取列表第二个元素的偏函数
val getSecond: PartialFunction[List[Int], Option[Int]] = {
case first :: second :: _ => Some(second)
}
2. 偏函数原理
scala
// 1. 定义的偏函数会被解析翻译
val getSecond: PartialFunction[List[Int], Option[Int]] = {
case first :: second :: _ => Some(second)
}
// =>
val getSecond = new PartialFunction[List[Int], Option[Int]] {
//检查输入参数是否合格
override def isDefinedAt(list: List[Int]): Boolean = list match {
case first :: second :: _ => true
case _ => false
}
//执行函数逻辑
override def apply(list: List[Int]): Option[Int] = list match {
case first :: second :: _ => Some(second)
}
}
// 2. 偏函数调用时采用 applyOrElse 方法,其逻辑为 if(ifDefinedAt(list)) apply(list) else default。如果输入参数满足条件,即 isDefinedAt 返回 true,则执行 apply 方法,否则执行 defalut 方法,default 方法为参数不满足要求的处理逻辑
getSecond.applyOrElse(List(1,2,3), (_: List[Int]) => None)
3. 应用案例
scala
object TestPartialFunction {
def main(args: Array[String]): Unit = {
// 使用偏函数实现绝对值方法
val positiveAbs: PartialFunction[Int, Int] = {
case x if x > 0 => x
}
val negativeAbs: PartialFunction[Int, Int] = {
case x if x < 0 => -x
}
val zeroAbs: PartialFunction[Int, Int] = {
case 0 => 0
}
def abs(x: Int): Int = (positiveAbs orElse negativeAbs orElse zeroAbs)(x)
println(abs(-22))
println(abs(18))
println(abs(0))
}
}