Chapter 15 《Case Classes and Pattern Matching》

樣例類是Scala對對象進(jìn)行模式匹配而不需要大量樣板代碼的方式导狡,對希望做模式匹配的類加上關(guān)鍵字case即可约巷。在Scala中如果類的定義體是空時,可以省去定義體的花括號旱捧。


樣例類
  • case class Var(String: x)
    帶有case關(guān)鍵字的類稱為樣例類独郎,生成了一個伴生對象踩麦,在這個對象中存在接收一個參數(shù)的apply工廠方法∶グ可以使用Var(x)而不是new Var(x)這樣繁瑣的語句來構(gòu)建新的對象谓谦;參數(shù)列表中的參數(shù)都是有隱式的val從而稱為類字段,編譯器以自然的方式實(shí)現(xiàn)toString,hashCodeequals方法贪婉,編譯器會添加一個copy用于拷貝反粥,使用到了帶名參數(shù)和缺省參數(shù)。樣例類的最主要貢獻(xiàn)還是可用于模式匹配疲迂。

模式匹配
    1. 模式匹配包含了一系列以case關(guān)鍵字打頭的可選分支才顿,每一個分支都包含一個模式,如果模式匹配了尤蒿,模式對應(yīng)的表達(dá)式就會被求值郑气。一個match表達(dá)式的求值過程是按照模式給出的順序逐一嘗試的。
    1. 常量模式匹配常量腰池,變量模式可以匹配任何值尾组,_表示通用模式,并不會引入一個變量名來指向這個值示弓。
    1. 還有就是構(gòu)造者模式讳侨,相對于ifelse的判斷,非常非常的方便避乏。case _ =>返回的是Unit類型的()值爷耀。

模式的種類
    1. 通配模式:_用于任何對象,還可以用來忽略某個對象中并不關(guān)心的局部拍皮,在模式中使用名字歹叮,其實(shí)是將等效的對象或者變量綁定在這個名字上。
    1. 常量模式:僅能匹配自己铆帽。任何字面量都可以作為常量模式來使用咆耿。單例對象也可以被當(dāng)做常量模式使用。val對象也可以被當(dāng)做常量模式使用爹橱。
    1. 變量模式:變量模式可以匹配任何對象萨螺,scala將匹配上的對象綁定為對應(yīng)的變量。綁定之后愧驱,可以利用這個變量來對對象做進(jìn)一步的處理慰技。
    • 注意:常量也可以有符號,比如Pi表示3.1415926组砚。在模式中通過標(biāo)識符的首個字母來表示名稱是常量還是變量吻商,所有以大寫字母打頭的標(biāo)識符對應(yīng)的模式都是常量模式。如果必須使用小寫字母來表示常量糟红,則需要使用反引號`pi`艾帐,在這種情況下乌叶,編譯器認(rèn)為pi是一個常量而不是變量,或者使用限定詞柒爸,比如obj.pi等准浴。`Scala中有兩種用途,第一是將首字母為小寫字母的標(biāo)識符作為常量模式使用捎稚,二是將關(guān)鍵字當(dāng)做普通的標(biāo)識符乐横,比如`Thread.`yield`
    1. 構(gòu)造方法模式:構(gòu)造方法模式是真正體現(xiàn)出模式匹配威力的地方今野。首先檢測被匹配的對象是否是該樣例類的實(shí)例晰奖,然后檢查該對象的構(gòu)造方法參數(shù)是否匹配模式中的構(gòu)造參數(shù)。構(gòu)造參數(shù)可能也是構(gòu)造模式腥泥,匹配時也對他們進(jìn)行檢測,因此匹配可以達(dá)到任意的深度啃匿。
    1. 序列模式:類似于樣例類匹配蛔外,可以跟序列類型做匹配。比如List或者Array溯乒,使用的語法是相同的夹厌,但可以在模式中給出任意數(shù)量的元素,因?yàn)?code>List和Array中含有接收任意參數(shù)的apply工廠構(gòu)造方法裆悄。在該模式中使用_*表示任意多個元素矛纹,如果要使用的話,必須放在構(gòu)造列表的最后面光稼。
    1. 元組模式或南,(a,b,c)可以匹配任意的三元組。
    1. 帶類型的模式:來代替類型檢測和類型轉(zhuǎn)換
      case s: String => s.length
      case m: Map[_,_] => m.size
    • 和帶類型的模式匹配但等效的方法是使用類型檢測并進(jìn)行強(qiáng)制轉(zhuǎn)換艾君。在Scala中類型檢測為expr.isInstanceOf[String]采够,expr.asInstanceOf[String]
    • 這其中涉及到的一個問題是類型擦除冰垄,ScalaJava一樣蹬癌,采用了擦除式的泛型,在運(yùn)行時并不會保留類型參數(shù)的信息虹茶,所以不能使用類似的語句來判斷逝薪,case m: Map[Int,Int] => m.size,編譯器只能判斷某個變量是不是Map蝴罪,而不能判斷這個Mapkeyvalue的類型董济。唯一例外的是數(shù)組,可以使用Array[String]這樣的來進(jìn)行驗(yàn)證洲炊,數(shù)組的元素類型被保存了感局。(where???)
  • 變量綁定尼啡,除了各自存在的變量模式外,還可以對任何其他模式添加變量询微,在模式之前添加變量名以及一個@符號崖瞭,即可得到。case UnOp("abs",e@Unop("abs",_)) => e撑毛,出現(xiàn)兩次求絕對值的操作书聚,只需要計(jì)算一次即可。

模式守衛(wèi)
  • Scala要求模式都是線性的:同一個模式變量在模式中只能出現(xiàn)一次藻雌。但是可以出現(xiàn)在多個case語句中雌续。比如
def simplifyAdd(e: Expr) =
 e match {
case BinOp("+", x, x) => BinOp("*", x, Number(2))
case _ => e
}

是不被允許的。使用模式守衛(wèi)胯杭,以if打頭驯杜。模式守衛(wèi)可以是任意的布爾表達(dá)式,一般會引用到模式中的變量做个,如果存在模式守衛(wèi)鸽心,這個匹配僅在模式守衛(wèi)為true的時候才會成功。

 def simplifyAdd(e: Expr) = e match {
case BinOp("+", x, y) if x == y => BinOp("*", x, Number(2))
case _ => e
}

模式重疊
  • 模式會按照順序進(jìn)行逐個嘗試居暖,所以在match中顽频,case的順序是很重要的。

密封類
    1. 當(dāng)編寫模式匹配的時候太闺,需要確保完整地覆蓋了所有可能的case糯景。為了使得Scala編譯器能夠幫助我們檢測是否窮舉了所有的情況,需要阻止子類的不可控再增加省骂。手段是將這些樣例類的超類標(biāo)記為sealed蟀淮,密封的。密封類除了在同一個文件中定義子類之外钞澳,不能添加新的子類灭贷。對于模式匹配非常有用,可以獲得更好的編譯器支持略贮。如果類被打算用于樣例類甚疟,一般會在類繼承關(guān)系的頂部類前加上sealed關(guān)鍵字。
    1. 如果在match中可以明確確定只處理某些情況逃延,對缺失不檢查的情況可以使用@unchecked注解览妖。在match的選擇器部分進(jìn)行添加(e: @unchecked) match {},對分支的覆蓋完整性會被壓制揽祥。

Option類型
  • Option表示可選值讽膏。將可選值解開的最常見方式是通過模式匹配,利用構(gòu)造器模式拄丰「鳎可以輕易的避免由null帶來的空指針異常問題俐末。

Pattern is everywhere
    1. Scala中很多地方都可以使用模式。利用可以使用元組模式 val (a, b) = (123, 234)奄侠,在處理樣例類的時候非常有用卓箫,如果知道要處理的樣例類是什么,可以使用一個模式來析構(gòu)它垄潮。
    1. 作為偏函數(shù)的case序列烹卒。用花括號包起來的一系列case可以用在任何允許出現(xiàn)函數(shù)字面量的地方。本質(zhì)上將弯洗,case序列就是一個函數(shù)字面量旅急,只不過是有多個入口,每個入口都有自己的參數(shù)列表牡整。每個case對應(yīng)該函數(shù)的一個入口藐吮,該入口的參數(shù)列表使用模式來指定。每個入口的邏輯主體是case右邊的部分逃贝。通過case序列得到的是一個偏函數(shù)炎码。偏函數(shù)如果應(yīng)用在它不支持的值上(雖然該參數(shù)的類型是正確的),會發(fā)生運(yùn)行時異常秋泳。
val second: List[Int] => Int = {
case x :: y :: _ => y
}
  • 如果要檢查偏函數(shù)對某個入?yún)⑹欠衽c定義,必須首先聲明處理的是偏函數(shù)攒菠。List[Int] => Int表示的是所有從整數(shù)列表到正數(shù)的函數(shù)迫皱,定義偏函數(shù)需要使用到
val second: PartialFunction[List[Int],Int] = {
case x :: y :: _ => y
}

PartialFunction中定義了一個isDefinedAt方法,可以用來檢查該函數(shù)對某個特定的數(shù)值是否有定義辖众。偏函數(shù)的典型應(yīng)用是模式匹配函數(shù)字面量(case序列)卓起,這個模式匹配會出現(xiàn)在PartialFunction定義中的兩處,一處用于apply凹炸,一處用于isDefinedAt戏阅。在默認(rèn)沒有聲明的情況下,函數(shù)字面量對應(yīng)的就是全函數(shù)啤它。

    1. for表達(dá)式中的模式
      for表達(dá)式中也可以使用元組表達(dá)式奕筐,構(gòu)造器表達(dá)式等等。如果在for表達(dá)式中迭代器代表的值沒有匹配到模式变骡,則該值會被直接丟棄离赫。

tips
  • a -> 1只是Tuple2(a, 1)的一個語法糖,->是一個方法調(diào)用塌碌,在Predef中有隱式類ArrowAssoc對值a進(jìn)行隱式轉(zhuǎn)換渊胸,可以調(diào)用->方法。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末台妆,一起剝皮案震驚了整個濱河市翎猛,隨后出現(xiàn)的幾起案子胖翰,更是在濱河造成了極大的恐慌,老刑警劉巖切厘,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萨咳,死亡現(xiàn)場離奇詭異,居然都是意外死亡迂卢,警方通過查閱死者的電腦和手機(jī)某弦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來而克,“玉大人靶壮,你說我怎么就攤上這事≡逼迹” “怎么了腾降?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碎绎。 經(jīng)常有香客問我,道長筋帖,這世上最難降的妖魔是什么奸晴? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮日麸,結(jié)果婚禮上代箭,老公的妹妹穿的比我還像新娘墩划。我一直安慰自己,他們只是感情好嗡综,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布乙帮。 她就那樣靜靜地躺著,像睡著了一般极景。 火紅的嫁衣襯著肌膚如雪察净。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天盼樟,我揣著相機(jī)與錄音塞绿,去河邊找鬼。 笑死恤批,一個胖子當(dāng)著我的面吹牛异吻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼诀浪,長吁一口氣:“原來是場噩夢啊……” “哼棋返!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起雷猪,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤睛竣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后求摇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體射沟,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年与境,在試婚紗的時候發(fā)現(xiàn)自己被綠了验夯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡摔刁,死狀恐怖挥转,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情共屈,我是刑警寧澤绑谣,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站拗引,受9級特大地震影響借宵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矾削,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一壤玫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧怔软,春花似錦、人聲如沸择镇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腻豌。三九已至家坎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吝梅,已是汗流浹背虱疏。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苏携,地道東北人做瞪。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親装蓬。 傳聞我的和親對象是個殘疾皇子著拭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容