Case 类(CASE CLASSES)
Case 类就像普通类一样,不过和普通类存在几个关键的区别,接下来我们将介绍一下。Case 类有助于对不可变的数据建模。后面也会介绍 Case 类在模式匹配中的应用。
定义 case 类
一个最简单的 case 类需要关键字 case class,名称标识符和参数列表(参数列可以为空):
case class Book(isbn: String)
val frankenstein = Book("978-0486282114")
注意是如何不用 new 关键字来实例化 case 类型的 book 类的。原因是因为 case 类有一个默认的 apply 方法用于构造对象。
当你创建带参数的 case 类时,这些参数的访问权限默认是公开(public)的,变量是 val 类型的。
case class Message(sender: String, recipient: String, body: String)
val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?")
println(message1.sender) // prints guillaume@quebec.ca
message1.sender = "travis@washington.us" // this line does not compile
因为 message1.sender 是 val 变量(不可变的),所以你不可以给它重新赋值。当然在 case 类中是可以使用 var 变量的,不过这种做法是让人难以接受的。
比较
case 类是根据内容结构进行比较而不是引用。
case class Message(sender: String, recipient: String, body: String)
val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
val messagesAreTheSame = message2 == message3 // true
即使 message2 和 message3 的引用指向不同的对象, 这两个对象的值还是相等的。
复制
你可以使用 copy 方法简单的创建一个 case 类实例的(浅层)副本。你也可以选择性的修改构造函数中的参数值。
case class Message(sender: String, recipient: String, body: String)
val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg")
val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr")
message5.sender // travis@washington.us
message5.recipient // claire@bourgogne.fr
message5.body // "Me zo o komz gant ma amezeg"
message4 中的 recipient 被赋值给了 message5 的 sender。但是 message4 的 body是直接复制到了 message5 的 body 中。
模式匹配(PATTERN MATCHING)
模式匹配是一种根据模式检查值的机制。一个成功的匹配也可以将值分解成它的组成部分。它是一个比 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 _ => "many"
}
上面 val x 是一个 0-10 之间的随机整数。match 操作符左边的 x 是左操作数,右边包含一个拥有四个 case 子句的表达式。表达式最后的 case 子句 _ 是用于捕捉比任何 case 数字 2 更大的情况 。 Cases 也称作备选方案(alternatives)。
含有一个参数的匹配表达式
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
matchTest(3) // many
matchTest(1) // 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 是一个抽象的超类,它有三个具体的 Notification 类型的实现类,分别是 Case 类 Email、SMS和 VoiceRecording。
现在我们可以在这些 case 类上进行模式匹配:
def showNotification(notification: Notification): String = {
notification match {
case Email(email, title, _) =>
s"You got an email from $email 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)) // 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(email,title,_) 中,字段 eamil 和 title 用在返回值中,但 body 被 _ 忽略。
模式警卫(Pattern guards)
模式警卫是一个简单的布尔表达式,用于使 case 子句更具体。只要在模式后添加 if <boolean expression> 表达式。
def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {
notification match {
case Email(email, _, _) if importantPeopleInfo.contains(email) =>
"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("867-5309", "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))
println(showImportantNotification(someVoiceRecording, importantPeopleInfo))
println(showImportantNotification(importantEmail, importantPeopleInfo))
println(showImportantNotification(importantSms, importantPeopleInfo))
在 case Email(email, _, _) if importantPeopleInfo.contains(email) 模式中,只有当 email 在 importantPeopleInfo 中才会匹配成功。
仅匹配类型
你可以像下面这样匹配类型:
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
}
方法 goIdle 依据不同的 Device 类型会有不同的行为。当 case 子句需要调用模式方法时,这会很有用。使用类型首字母作为名称标识符是惯例。(例如 case 类中 p 和 c )
密封类(Sealed classes)
Traits 和 classes 都可以被标记为 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类表示的代数表达式的匹配是最有用的。通过使用提取器对象(extractor objects)中的 unapply 方法,Scala 也允许独立于 case 类(case classes)的模式定义。
Scala Case类详解

本文介绍了Scala中的Case类,包括如何定义Case类、它们的特点及如何使用。此外还讲解了Case类在模式匹配中的应用,包括模式匹配的基础语法、匹配Case类、使用模式警卫以及仅匹配类型。
3万+

被折叠的 条评论
为什么被折叠?



