Scala case class學(xué)習(xí)

case class介紹

樣例類(case class)適合用于不可變的數(shù)據(jù)。它是一種特殊的類,能夠被優(yōu)化以用于模式匹配。

case class定義

case class Book(name: String) {
  def printBookName(): Unit = {
    println(name)
  }
}

object BookTest {
  def main(args: Array[String]): Unit = {
    val book = Book("Java入門到放棄")
    book.printBookName()
  }
}

在實(shí)例化case class類時(shí),不需要使用關(guān)鍵字New,case class類編譯成class文件之后會(huì)自動(dòng)生成apply方法,這個(gè)方法負(fù)責(zé)對(duì)象的創(chuàng)建。
通過(guò)JD-GUI工具可以查看編譯后的.class文件(有興趣的可以自己看下)。
Scala自動(dòng)為Book生成了apply靜態(tài)方法,里面調(diào)用了Book$類的apply方法用來(lái)生成Book對(duì)象。

Book$類的截圖


case class類的參數(shù)都是可以直接訪問(wèn)的val(不能被修改),但是實(shí)際上編譯成的class字節(jié)碼會(huì)對(duì)book.name轉(zhuǎn)成book.name()方法調(diào)用。如下圖所示,name聲明的時(shí)候是加了final關(guān)鍵字,并且生成了對(duì)應(yīng)的name()方法。



printBookName()方法中使用到的book.name實(shí)際上是調(diào)用的name()方法。


模式匹配

模式匹配是檢查某個(gè)值(value)是否匹配某一個(gè)模式的機(jī)制,一個(gè)成功的匹配同時(shí)會(huì)將匹配值解構(gòu)為其組成部分。它是Java中的switch語(yǔ)句的升級(jí)版。

語(yǔ)法

一個(gè)模式匹配語(yǔ)句包括一個(gè)待匹配的值,match關(guān)鍵字,以及至少一個(gè)case語(yǔ)句。示例如下:

def matchTest(x: Int): String = x match {
    case 1 => "one"
    case 2 => "two"
    case _ => "many"
  }

case class的匹配

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 是一個(gè)虛基類,它有三個(gè)具體的子類Email, SMS和VoiceRecording,我們可以在這些Case Class類上使用模式匹配:

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! Link: $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函數(shù)接受一個(gè)抽象類Notification對(duì)象作為輸入?yún)?shù),然后匹配其具體類型。(也就是判斷它是一個(gè)Email,SMS,還是VoiceRecording)。在case Email(email, title, _ )中,對(duì)象的email和title屬性在返回值中被使用,而body屬性則被忽略,故使用_代替。
另外需要注意的一點(diǎn)是case Email(email, title, _)語(yǔ)句實(shí)際上是使用了Email提取器對(duì)象的unApply方法,這個(gè)方法也是Scala編譯字節(jié)碼的時(shí)候自動(dòng)生成的,它會(huì)去提取匹配到的Email對(duì)象的sender和title屬性填充到email和title屬性上。
showNotification方法還可以等價(jià)寫成下面這種形式,只匹配類型,而不使用Scala的提取器方式。

def showNotification(notification: Notification): String = {
    notification match {
      case e: Email =>
        s"You got an email from ${e.sender} with title: ${e.title}"
      case s: SMS =>
        s"You got an SMS from ${s.caller}! Message: ${s.message}"
      case vr: VoiceRecording =>
        s"you received a Voice Recording from ${vr.contactName}! Link: ${vr.link}"
    }
  }

模式守衛(wèi)

為了讓匹配更加具體,可以使用模式守衛(wèi),也就是在模式后面加上if表達(dá)式。

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", "vr.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)) // You got an SMS from special someone!
println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) //you received a Voice Recording from Tom! Link: vr.org/id/123
println(showImportantNotification(importantEmail, importantPeopleInfo)) // You got an email from special someone!
println(showImportantNotification(importantSms, importantPeopleInfo)) //You got an SMS from special someone!

在case Email(email, _ , _ ) if importantPeopleInfo.contains(email)中,除了要求notification是Email類型外,還需要email在重要人物列表importantPeopleInfo中,才會(huì)匹配到該模式。

密封類

特質(zhì)(trait)和類(class)可以用sealed標(biāo)記為密封的,這意味著其所有子類都必須與之定義在相同文件中,從而保證所有子類型都是已知的。

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"
}

參考資料

  1. 案例類 https://docs.scala-lang.org/zh-cn/tour/case-classes.html
  2. 模式匹配 https://docs.scala-lang.org/zh-cn/tour/pattern-matching.html
  3. https://blog.csdn.net/lovehuangjiaju/article/details/47176829
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容