模式匹配是一个检查值是否属于某个模式的机制。成功的匹配可以将一个值分解成多个组成部分。它相当于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类型进行模式匹配