好程序員大數據教程分享Scala系列之模式匹配和樣例類

好程序員大數據教程分享Scala系列之模式匹配和樣例類
1.樣例類
在Scala中樣例類是一中特殊的類,樣例類是不可變的,
可以通過值進行比較,可用於模式匹配。
定義一個樣例類:
1.構造器中每一個參數都是val,除非顯示地聲明爲var
2.伴生對象提供apply ,讓你不使用new關鍵字就能構造出相應的對象
case class Point(x: Int, y: Int)
創建樣例類對象:
val point = Point(1, 2)
val anotherPoint = Point(1, 2)
val yetAnotherPoint = Point(2, 2)
//訪問對象值
point.x
point.x =1 //不可以
通過值對樣例類對象進行比較:
if (point == anotherPoint) {
println(point + " and " + anotherPoint + " are the same.")
} else {
println(point + " and " + anotherPoint + " are different.")
}
// Point(1,2) 和 Point(1,2)一樣的.

if (point == yetAnotherPoint) {
println(point + " and " + yetAnotherPoint + " are the same.")
} else {
println(point + " and " + yetAnotherPoint + " are different.")
}
// Point(1,2)和Point(2,2)是不同的.
樣例類的拷貝
You can create a (shallow) copy of an instance of a case class simply by using the copy method. You can optionally change the constructor arguments.
case class Message(sender: String, recipient: String, body: String)
val message4 = Message("[email protected]", "[email protected]", "Me zo o komz gant ma amezeg")
val message5 = message4.copy(sender = message4.recipient, recipient = "[email protected]")
message5.sender // [email protected]
message5.recipient // [email protected]
message5.body // "Me zo o komz gant ma amezeg"
在模式匹配中使用樣例類:
abstract class Amount
// 繼承了普通類的兩個樣例類
case class Dollar(value: Double) extends Amount
case class Currency(value: Double, unit: String) extends Amount
case object Nothing extends Amount
object CaseClassDemo {

def main(args: Array[String]): Unit = {
val amt = new Dollar(10);
patternMatch(amt)
}
def patternMatch(amt: Amount) {
amt match {
case Dollar(v) => println("$" + v)
case Currency(_, u) => println("Oh noes, I got " + u)
case Nothing => println("nothing") //樣例對象沒有()
}
}
}
聲明樣例類 ,以下幾件事會自動發生:
1.提供unapply方法,讓模式匹配可以工作
2.生成toString equals hashCode copy 方法,除非顯示給出這些方法的定義。
2.模式匹配
1.更好的switch
Scala中類似Java的switch代碼:
注意:
Scala的模式匹配只會匹配到一個分支,不需要使用break語句,因爲它不會掉入到下一個分支。 match是表達式,與if表達式一樣,是有值的:
object PatternDemo {

def main(args: Array[String]): Unit = {
var sign = 0
val ch: Char = 'p'
val valchar = 'p'
var digit = 0

//match 是表達式
ch match {
case '+' => sign = 1
case '-' => sign = -1
//使用|分割多個選項
case '*' | 'x' => sign = 2
//可以使用變量
//如果case關鍵字後面跟着一個變量名,那麼匹配的表達式會被賦值給那個變量。
case valchar => sign = 3
//case 類似Java中的default
// 如果沒有模式能匹配,會拋出MacthError
//可以給模式添加守衛
case
if Character.isDigit(ch) => digit = Character.digit(ch, 10)
}
println("sign = "+ sign)
}

}
1常量模式(constant patterns) 包含常量變量和常量字面量
scala> val site = "alibaba.com"
scala> site match { case "alibaba.com" => println("ok") }
scala> val ALIBABA="alibaba.com"
//注意這裏常量必須以大寫字母開頭
scala> def foo(s:String) { s match { case ALIBABA => println("ok") } }
常量模式和普通的 if 比較兩個對象是否相等(equals) 沒有區別,並沒有感覺到什麼威力
2 變量模式(variable patterns)
確切的說單純的變量模式沒有匹配判斷的過程,只是把傳入的對象給起了一個新的變量名。
scala> site match { case whateverName => println(whateverName) }
上面把要匹配的 site對象用 whateverName 變量名代替,所以它總會匹配成功。不過這裏有個約定,對於變量,要求必須是以小寫字母開頭,否則會把它對待成一個常量變量,比如上面的whateverName 如果寫成WhateverName就會去找這個WhateverName的變量,如果找到則比較相等性,找不到則出錯。
變量模式通常不會單獨使用,而是在多種模式組合時使用,比如
List(1,2) match{ case List(x,2) => println(x) }
裏面的x就是對匹配到的第一個元素用變量x標記。
3 通配符模式(wildcard patterns)
通配符用下劃線表示:"" ,可以理解成一個特殊的變量或佔位符。 單純的通配符模式通常在模式匹配的最後一行出現,case => 它可以匹配任何對象,用於處理所有其它匹配不成功的情況。 通配符模式也常和其他模式組合使用:
scala> List(1,2,3) match{ case List(,,3) => println("ok") }
上面的 List(,,3) 裏用了2個通配符表示第一個和第二個元素,這2個元素可以是任意類型 通配符通常用於代表所不關心的部分,它不像變量模式可以後續的邏輯中使用這個變量。
4.樣例類匹配
//定義樣例類
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

//基於樣例類的模式匹配
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)) //結果: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
2.帶守衛的模式
增加布爾表達式或者條件表達式使得匹配更具體。
def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {
notification match {
//僅匹配email在importantPeople列表裏的內容
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", "[email protected]")

val someSms = SMS("867-5309", "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))
println(showImportantNotification(someVoiceRecording, importantPeopleInfo))
println(showImportantNotification(importantEmail, importantPeopleInfo))
println(showImportantNotification(importantSms, importantPeopleInfo))
5.類型匹配
可以對表達式類型進行匹配:
val arr = Array("hs", 1, 2.0, 'a')
val obj = arr(Random.nextInt(4))
println(obj)
obj match {
case x: Int => println(x)
case s: String => println(s.toUpperCase)
case : Double => println(Int.MaxValue)
case
=> 0
}
注意: 當你在匹配類型的時候,必須給出一個變量名,否則你將會拿對象本身來進行匹配:
obj match {
case : BigInt => Int.MaxValue // 匹配任何類型爲BigInt的對象
case BigInt => -1 // 匹配類型爲Class的BigInt對象
}
匹配發生在運行期,Java虛擬機中泛型的類型信息是被擦掉的。因此,你不能用類型來匹配特定的Map類型。
case m: Map[String, Int] => ... // error
// 可以匹配一個通用的映射
case m: Map[
, _] => ... // OK

// 但是數組作爲特殊情況,它的類型信息是完好的,可以匹配到Array[Int]
case m: Array[Int] => ... // OK
3.匹配數組、列表、元組
數組匹配
val arr1 = Array(1,1)
val res = arr1 match {
case Array(0) => "0"
//匹配包含0的數組
case Array(x, y) => s"$x $y"
// 匹配任何帶有兩個元素的數組,並將元素綁定到x和y
case Array(0, *) => "0..."
//匹配任何以0開始的數組
case
=> "something else"
}
列表匹配
val lst = List(1,2)
val res2 = list match {
case 0 :: Nil => "0"
case x :: y :: Nil => x + " " + y
case 0 :: tail => "0 ..."
case => "something else"
}
元組匹配
var pair = (1,2)
val res3 = pair match {
case (0,
) => "0 ..."
case (y, 0) => s"$y 0"
case _ => "neither is 0"
}
4.Sealed 類(密封類,備選)
Scala中,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"
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章