03-scala模式匹配

149 阅读2分钟

基础类型匹配

首先我们看下最基础的匹配:

package example

import scala.annotation.switch

object MyExample {
  
  def main(args: Array[String]): Unit = {
    val n = 11
    val res = n match {
      case 1 => n + 1
      case 2 | 3 | 4 => n + 2  // 可以匹配多个
      case 10 => n * 10
      case _ => {  // 匹配不上
        println("miss match")
        n - 1
      }
    }
    println(res)
  }
}

代码输出:

miss match
10

Option类型匹配

一般来说,我们不要在scala中使用空值,更推荐使用Option的方式。使用Option必然经常使用match,因此直接在这里介绍。直接给出代码实例:

package example

object MyExample {

  def message(n: Int): Option[String] = {
    if (n > 0) {
      Option("hello foo")
    } else {
      None
    }
  }

  def getMessage(message: Option[String]): Unit = {
    message match {
      case Some(msg) => println(s"Get msg: $msg")
      case None => println("Get None !!")
    }
  }

  def main(args: Array[String]): Unit = {
    val msg1 = message(1)
    val msg2 = message(-1)
    getMessage(msg1)
    getMessage(msg2)
  }
}

代码输出:

Get msg: hello foo
Get None !!

case class 匹配

之后,我们看下case class的匹配,先看最基本的一种:

package example

// 声明为sealed, 子类必须在同一个文件中
sealed abstract class Notification

case class Email(sender: String, title: String, body: String) extends Notification

case class WeChat(caller: String, message: String) extends Notification

case class VoiceRecording(contentName: String, link: String) extends Notification

case class WrongMsg(content: String) extends Notification

object MyExample {

  def getNotification(notification: Notification): Unit = {
    notification match {
      case Email(sender, title, body) => {
        println(s"$sender send you a email, title: $title, body: $body")
      }
      case WeChat(caller, _) => {  // _ 省略内容
        println(s"$caller gives you a wechat message")
      }
      case VoiceRecording(_, link) => {  // _ 省略内容
        println(s"you have a voice, link is $link")
      }
      case _ => {
        println("wrong type msg")
      }
    }
  }
  
  def main(args: Array[String]): Unit = {
    val email = Email("foo", "Hello", "Hello, I am foo")
    val weChat = WeChat("foo2", "Hi foo")
    val voiceRecording = VoiceRecording("foo voice", "www.foovoice.link")
    val wrongMsg = WrongMsg("")
    getNotification(email)
    getNotification(weChat)
    getNotification(voiceRecording)
    getNotification(wrongMsg)
  }
}

代码输出:

foo send you a email, title: Hello, body: Hello, I am foo
foo2 gives you a wechat message
you have a voice, link is www.foovoice.link
wrong type msg

模式守卫

有些情况下,我们想要让匹配的case class再满足某种条件,才会继续执行后面的代码块,举个例子:

package example

import scala.annotation.switch

// 声明为sealed, 子类必须在同一个文件中
sealed abstract class Animal

case class Cat(name: String, age: Int) extends Animal

case class Dog(name: String, master: String) extends Animal

case class Bird(name: String, canFly: Boolean) extends Animal

object MyExample {
  
  def getAnimal(animal: Animal): Unit = {
    animal match {
      case Bird(name, canFly) if (canFly) => println(s"Bird $name can fly")
      case Dog(name, master) if (master.length > 0) => println(s"Dog $name has master")
      case Cat(name, age) => println(s"Cat $name is $age years old")
      case _ => {}
    }
  }

  def main(args: Array[String]): Unit = {
    val bird = Bird("foo-bird", true)
    val dog = Dog("foo-dog", "")  // 不会执行
    val cat = Cat("foo-cat", 3)
    getAnimal(bird)
    getAnimal(dog)
    getAnimal(cat)
  }
}

代码输出:

Bird foo-bird can fly
Cat foo-cat is 3 years old

仅类型匹配

我们可以仅仅匹配类型,然后对匹配上类型的变量执行操作,举个例子:

package example

import scala.annotation.switch

// 声明为sealed, 子类必须在同一个文件中
sealed abstract class Device

case class Phone(name: String) extends Device

case class PC(name: String) extends Device

case class Watch(name: String) extends Device

object MyExample {

  def getDevice(device: Device): Unit = {
    device match {
      case phone: Phone => println(s"Phone: ${phone.toString}")
      case pc: PC => println(s"PC: ${pc.toString}")
      case watch: Watch => println(s"Watch: ${watch.toString}")
      case _ => {}
    }
  }

  def main(args: Array[String]): Unit = {
    val phone = Phone("iPhone")
    val pc = PC("MacBookPro")
    val watch = Watch("iWatch")
    getDevice(phone)
    getDevice(pc)
    getDevice(watch)
  }
}

代码输出:

Phone: Phone(iPhone)
PC: PC(MacBookPro)
Watch: Watch(iWatch)