Scala模式匹配(Pattern Matching)——match - case

本文详细介绍了Scala中的模式匹配特性,从常量匹配、变量匹配、元组匹配、case class匹配、List匹配、类型标识符匹配、名称绑定、多模式匹配、守卫等多个角度展开,展示了其在处理复杂条件和数据结构时的强大能力。同时,文章提醒读者注意JVM的类型擦除问题,并给出了多个示例代码,帮助理解并提升代码的优雅性和可读性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

模式匹配是Scala中一项非常强大的功能,它类似于Java和C中的switch - case,但功能比switch - case要强上许多。在Scala中,everything is pattern,任何东西都可以是模式。这里分享一些模式匹配的trick。

一定要看注意事项啊!!!

一、常量匹配

模式匹配使用match关键字来开启,使用case关键字来枚举情况。 Scala中的模式匹配可以有返回值,Java中Jdk 14或以上版本的增强switch也可以有返回值。

这种匹配模式与Java或者C中的switch就很像了,编译器会用变量的值和常量按顺序进行比较,符合情况执行后续代码。

object Test {

  // 这里执行结果是“zero”,因为“zero”在“零”的前面
  def main(args: Array[String]): Unit = {
    val num = 0
    println(num match {
      case 0 => "zero"
      case 0 => "零"
      case 1 => "one"
      case 2 => "two"
      case 3 => "three"
    })
  }

}

二、变量匹配

1. 变量命名

在每一种匹配到的情况中,我们可以为匹配到的数据命名为一个变量,在后面的代码中使用它。该变量的使用范围仅局限于该case。

object Test {

  def main(args: Array[String]): Unit = {
    val str = "I have something."
    str match {
      case something => println(s"I said:\"$something\"")
    }
  }

}

在Scala的模式匹配中没有default关键字,默认情况使用通配符——下划线(_)来处理。在其他case都没有匹配到是匹配该case,若没有通配case,抛出异常。

object Test {

  def main(args: Array[String]): Unit = {
    val str = "This is a string value."
    str match {
      case _ => println("Cover your ass!")
    }
  }

}

当使用模式匹配时,建议添加通配case,不然像下面的代码:

object Test {

  def main(args: Array[String]): Unit = {
    val str = "I have something."
    str match {
      case "Fk, Python!" => println(s"Yeah, I agree.")
    }
  }

}

会抛出异常导致,crush!!!

三、元组匹配

前两种匹配模式都适用于元组匹配。下划线(_)是通配符,表示该位置可以是任何数字。

object Test {

  def main(args: Array[String]): Unit = {
    val tuple = (1, 2, 3)
    tuple match {
      case (0, 0, 0)          => println("They are all zeros!!!")
      case (_, 5, _)          => println("5 is in middle.")
      case (num1, num2, num3) => 
        if (num1 == num2 - 1 && num2 == num3 - 1) println("Ohhhh, it's a straight!")
        else println("Just a tuple.")
    }
  }

}

元组匹配也可以进行嵌套匹配,如下。

case (_, (value, 3)) => ???

四、case class构造器匹配

对case class进行匹配。case class一般是像Person、Student、Animal这种bean类型的数据(也有人,比如我,喜欢叫pojo类型,或是domain类型等),匹配这种数据类型可以对其构造器进行匹配。

object Test {

  case class GraphicsCard(clazz: String, typee: Int, price: Double)

  def main(args: Array[String]): Unit = {
    val gpu = GraphicsCard("RTX", 3090, 29999.01)
    gpu match {
      case GraphicsCard("GTX", _, _)         => println("Sweet, honey!!!")
      case GraphicsCard("AMD", _, _)         => println("Yes.")
      case GraphicsCard(clazz, typee, price) => println(s"$clazz $typee is Out of Stock!" +
                                                s" Even if the price is $price yuan")
    }
  }

}

五、List匹配

当List中有很多元素时,我们并不需要匹配所有的元素时,可以使用下划线加星号(_*)的形式表示任意个元素,包括零个,但是只能用于末尾

不光List可以这么匹配,Array等一些集合也可以这么匹配。

object Test {

  def main(args: Array[String]): Unit = {
    val list = List.empty[Int]
    list match {
      // 匹配空列表
      case Nil         => println("This is a EMPTY LIST.")
      // 匹配以0开头的列表
      case List(0, _*) => println("Start with 0.")
      // 匹配只有一个元素的列表
      case List(e)     => println(s"I only have one element. It's $e")
      // 匹配任意列表
      case List(_*)    => println("Just a list.")
      // 默认匹配项
      case _           => println("Cover your ass.")
    }
  }

}

六、类型标识符匹配

你可以为每个case使用一个标识符,当匹配对象类型与该case类型相同时,执行case代码。

object Test {

  def main(args: Array[String]): Unit = {
    val collection: Traversable[Int] = List(1, 2, 3)
    collection match {
      case list: List[Int]   => println(s"This is a list ==> $list")
      case queue: Queue[Int] => println(s"This is a queue ==> $queue")
      case _                 => println("Cover your ass.")
    }
  }

}

七、名称绑定

我们可以使用@为一些值或是一些属性绑定名称,在后面的case代码中可以使用。

object Test {

  def main(args: Array[String]): Unit = {
    val tuple = (1, 2, (3, 4, (5, 6)))
    tuple match {
      case (_, _, sub1 @ (_, _, sub2 @ (_ , _))) => println(s"Sub-tuple is $sub1 and $sub2")
      case _                                     => println("Cover your ass!!!")
    }
  }

}

八、多模式匹配

当多种情况都是同一种处理方式时,可以使用多模式匹配减少代码行数。使用竖线(|)将不同的情况隔开,当满足其中一种情况时就会执行case。

object Test {

  def main(args: Array[String]): Unit = {
    val num = 1
    num match {
      case 1 | 3 | 5 | 7 | 9 => println("They are odd!")
      case 0 | 2 | 4 | 6 | 8 => println("They are even!")
      case _                 => println("Cover your ass.")
    }
  }

}

九、守卫

跟for循环一样,case中也可以添加if守卫,模式匹配时必须模式对应并且满足守卫要求才能执行case。

这如果后面的代码中不需要用到变量,就可以用下划线(_)代替,不用起名字了。

object Test {

  case class GraphicsCard(clazz: String, typee: Int, price: Double)

  def main(args: Array[String]): Unit = {
    val value: AnyVal = 1
    value match {
      case x: Int if x % 2 == 1 => println("It is odd!")
      case x: Int if x % 2 == 0 => println("It is even!")
      case _: Double => println("Who care.")
      case _ => println("Cover your ass.")
    }
  }

}

十、一些注意事项

1. 常用模式匹配

我们在Scala的很多地方其实都能看到模式匹配的身影。比如说,偏函数,try - catch等。

常用模式匹配可以避免出现很长的if - else的情况,提高代码的整洁度和可读性,非常的优雅~

并且Scala中的模式匹配相较于Java、C的switch - case来说真的非常强大了,足以应付绝大多是情况。

2. 使用模式匹配常见变量

object Test {

  def main(args: Array[String]): Unit = {
    val array = Array(1, 2, 3)
    val Array(a, b, _*) = array
    println(s"a ==> $a\nb ==> $b")
  }

}

3. 注意JVM的类型擦除

由于Scala是一门JVM语言,依赖于Java的JVM,所以会受到JVM的限制。

观察下面代码,思考会输出什么?

object Test {

  def main(args: Array[String]): Unit = {
    val array = List(1, 2, 3)
    val res = array match {
      case a: List[String] => "String"
      case a: List[Int]    => "Int"
    }
    println(res)
  }

}

相信有人会说输出“Int”。但是其实输出的是“String”。

 因为在Java中,创建一个泛型对象时虽然给定了泛型的类型,但是这个类型在JVM中时会被擦除。因此这个问题在Scala中也同样存在,因为Scala依赖于JVM。

上面代码其实相当于:

val res = array match {
  case a: List[Any] => "String"
  case a: List[Any] => "Int"
}

因此总是会得到“String”。在使用时需要特别注意。

结语

模式匹配的内容还是有点多的,要熟练掌握还是需要平常多多使用。

如果有大佬发现文章叙述不对或是有不好的地方可以指出,非常感谢。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值