scala之旅-核心语言特性【模式匹配】(十四)

模式匹配是一个检查值是否属于某个模式的机制。成功的匹配可以将一个值分解成多个组成部分。它相当于java中的switch的一个强化版本,并且可以代替if/else的很多场景。

语法

一个匹配表达式有一个值,然后接一个match关键字,最后接一个case 短语

import scala.util.Random

val x: Int = Random.nextInt(10)

x match {
  case 0 => "zero"
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}

上面的 val x 是一个间于0到10的整型随机数。x 成为操作符 match 左边操作的对象,右边则是4个case 表达式。最后一个 case _ 是一个用来捕获其他所有Int值的。 case 也可以有选择地进行调用。

匹配表达式有一个值。

def matchTest(x: Int): String = x match {
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}
matchTest(3)  // prints other
matchTest(1)  // prints one

这个匹配表达式是String类型,因为它的所有的case 都是返回的String类型。因为函数 matchTest返回一个String 类型。

case类中匹配

case 类在模式匹配中尤为的友好。

abstract class Notification

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

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

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

Notification 是一个抽象超类,他一个三个具体的case子类实现类它: Email ,SMS 和 VoiceRecording。 现在我们用这些case类型进行模式匹配:

def showNotification(notification: Notification): String = {
  notification match {
    case Email(sender, title, _) =>
      s"You got an email from $sender with title: $title"
    case SMS(number, message) =>
      s"You got an SMS from $number! Message: $message"
    case VoiceRecording(name, link) =>
      s"You received a Voice Recording from $name! Click the link to hear it: $link"
  }
}
val someSms = SMS("12345", "Are you there?")
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")

println(showNotification(someSms))  // prints You got an SMS from 12345! Message: Are you there?

println(showNotification(someVoiceRecording))  // prints You received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123

方法 showNotification 传递一个Notification抽象类作为形参,然后匹配Notification类型(通过识别出它是Email,SMS还是VoiceRecording)。在 case Email(sender,title,_) 中,属性sender和title被用到了返回值里面,但是 body属性被一个 _ 忽略了。

模式监视

模式监视是一个简单的布尔表达式,一般被用来让case 更加明确。 只需要添加 if <布尔表达式> 在模式后面。

def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {
  notification match {
    case Email(sender, _, _) if importantPeopleInfo.contains(sender) =>
      "You got an email from special someone!"
    case SMS(number, _) if importantPeopleInfo.contains(number) =>
      "You got an SMS from special someone!"
    case other =>
      showNotification(other) // nothing special, delegate to our original showNotification function
  }
}

val importantPeopleInfo = Seq("867-5309", "jenny@gmail.com")

val someSms = SMS("123-4567", "Are you there?")
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!")
val importantSms = SMS("867-5309", "I'm here! Where are you?")

println(showImportantNotification(someSms, importantPeopleInfo)) // prints You got an SMS from 123-4567! Message: Are you there?
println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) // prints You received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
println(showImportantNotification(importantEmail, importantPeopleInfo)) // prints You got an email from special someone!

println(showImportantNotification(importantSms, importantPeopleInfo)) // prints You got an SMS from special someone!

在 case Email(sender,_,_) if importantPeopleInfo.contains(sender) 中,只有当 sender 在重要人列表中才进行匹配。

只做类型匹配

你可以像下面这样进行匹配:

abstract class Device
case class Phone(model: String) extends Device {
  def screenOff = "Turning screen off"
}
case class Computer(model: String) extends Device {
  def screenSaverOn = "Turning screen saver on..."
}

def goIdle(device: Device) = device match {
  case p: Phone => p.screenOff
  case c: Computer => c.screenSaverOn
}

def goIdle 依赖于 Device的类型会有一个不同行为。这个可以让你在一个模式匹配中根据类型调用对应方法。在匹配前面用标志符可以很简便的进行使用(这里用的p和c做的标志符)

sealed密封类

特性和类可以用sealed标记,这表明所有的子类型必须要声明在同一个文件里面。这样可以确保所有的子类型都是可知的。

sealed abstract class Furniture
case class Couch() extends Furniture
case class Chair() extends Furniture

def findPlaceToSit(piece: Furniture): String = piece match {
  case a: Couch => "Lie on the couch"
  case b: Chair => "Sit on the chair"
}

这在模式匹配中很有用,因为我们不需要再用一个默认值捕捉其他的类型了。 

笔记

Scala的模式匹配语句对于通过case类表示的代数类型最为有用。Scala还允许使用unapply和方法中对象提取器进行定义独立的case类型进行模式匹配

原文地址:https://www.cnblogs.com/zhouwenyang/p/13890157.html