本文介绍了case class和以及case class在模式匹配中如何使用。
case class介绍
样例类(case class)适合用于不可变的数据。它是一种特殊的类,能够被优化以用于模式匹配。
case class定义1
2
3
4
5
6
7
8
9
10
11
12case class Book(name: String) {
def printBookName(): Unit = {
println(name)
}
}
object BookTest {
def main(args: Array[String]): Unit = {
val book = Book("Java入门到放弃")
book.printBookName()
}
}
在实例化case class类时,不需要使用关键字New,case class类编译成class文件之后会自动生成apply方法,这个方法负责对象的创建。
通过JD-GUI工具可以查看编译后的.class文件(有兴趣的可以自己看下)。
Scala自动为Book生成了apply静态方法,里面调用了Book$类的apply方法用来生成Book对象。
Book$类的截图
case class类的参数都是可以直接访问的val(不能被修改),但是实际上编译成的class字节码会对book.name转成book.name()方法调用。如下图所示,name声明的时候是加了final关键字,并且生成了对应的name()方法。
printBookName()方法中使用到的book.name实际上是调用的name()方法。
模式匹配
模式匹配是检查某个值(value)是否匹配某一个模式的机制,一个成功的匹配同时会将匹配值解构为其组成部分。它是Java中的switch语句的升级版。
语法
一个模式匹配语句包括一个待匹配的值,match关键字,以及至少一个case语句。示例如下:
1 | def matchTest(x: Int): String = x match { |
case class的匹配
1 | abstract class Notification |
Notification 是一个虚基类,它有三个具体的子类Email, SMS和VoiceRecording,我们可以在这些Case Class类上使用模式匹配:
1 | def showNotification(notification: Notification): String = { |
showNotification函数接受一个抽象类Notification对象作为输入参数,然后匹配其具体类型。(也就是判断它是一个Email,SMS,还是VoiceRecording)。在case Email(email, title, _ )中,对象的email和title属性在返回值中被使用,而body属性则被忽略,故使用_代替。
另外需要注意的一点是case Email(email, title, _)语句实际上是使用了Email提取器对象的unApply方法,这个方法也是Scala编译字节码的时候自动生成的,它会去提取匹配到的Email对象的sender和title属性填充到email和title属性上。
showNotification方法还可以等价写成下面这种形式,只匹配类型,而不使用Scala的提取器方式。
1 | def showNotification(notification: Notification): String = { |
模式守卫
为了让匹配更加具体,可以使用模式守卫,也就是在模式后面加上if表达式。
1 | def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { |
在case Email(email, _ , _ ) if importantPeopleInfo.contains(email)中,除了要求notification是Email类型外,还需要email在重要人物列表importantPeopleInfo中,才会匹配到该模式。
密封类
特质(trait)和类(class)可以用sealed标记为密封的,这意味着其所有子类都必须与之定义在相同文件中,从而保证所有子类型都是已知的。
1 | sealed abstract class Furniture |