模式匹配是检查一个值是否匹配某种模式的机制。一个成功的匹配可以把一个值解析成它的组成部分。它相当于Java中switch
的增强版,可以以此来替代一堆的if/else语句。
语法
一个match表达式有个待匹配的值、match
关键字以及至少一个case
从句。
1 | import scala.util.Random |
上面的val x
是0到10之间的要给随机整数。x
是match
操作符的左操作数,右边是4个样例组成的表达式。最后一个样例_
匹配了大于2的所有数字。这些样例也被称为_代值_。
match表达式有一个返回值。
1 | def matchTest(x: Int): String = x match { |
这个match表达式返回一个String类型,那是因为所有的case都返回String。所以函数matchTest
便返回一个String。
匹配样例类
样例类在模式匹配中特别有用。
1 | abstract class Notification |
Notification
是一个抽象父类,另外有3个Notification
类型的实现,分别是样例类Email
、SMS
和VoiceRecording
。现在我们可以使用这些样例类进行模式匹配。
1 | def showNotification(notification: Notification): String = { |
函数showNotification
接受一个抽象类型Notification
,并且用来匹配Notification
类型(它会判断出到底是一个Email
、SMS
,还是一个VoiceRecording
)。在case Email(email, title, _)
中email
和title
用于返回值,而body
则因_
而被忽略。
模式守卫
模式守卫是简单的布尔表达式,使得case子句更具体,要做到这点只需要在模式后添加if <boolean expression>
。
1 | def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { |
在case Email(email, _, _) if importantPeopleInfo.contains(email)
中,仅当email
在重要人士的的列表中,该模式才会匹配上。
类型匹配
你可以像下面这样匹配类型:
1 | abstract class Device |
def goIdle
根据Device
的类型不同会有不同的行为。当模式中需要调用方法时,这会很有用。一般惯例是使用类型的首字母来作为case标识符(如本例子中的p
和c
)。
密封类
特质和类都可以被标记为sealed
,这个时候也意味着它的所有子类必须在相同的文件中进行声明。这样做可以保证所有的子类型都是已知的。
1 | sealed abstract class Furniture |
这种做法对于模式匹配来说非常有用,因为我们不再需要一个额外的“case all”子句了。
注意
Scala的模式匹配语句对于由样例类表示的代数类型的匹配时很有用。另外Scala也允许通过使用提取器对象的unapply
方法,来定义有别于样例类的的模式。