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 Notificationcase 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", "[email protected]") val someSms = SMS("123-4567", "Are you there?") val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") val importantEmail = Email("[email protected]", "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,_,_) ifimportantPeopleInfo.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型別進行模式匹配