樣例類是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,hashCode
和equals
方法贪婉,編譯器會添加一個copy
用于拷貝反粥,使用到了帶名參數(shù)和缺省參數(shù)。樣例類的最主要貢獻(xiàn)還是可用于模式匹配疲迂。
模式匹配
- 模式匹配包含了一系列以
case
關(guān)鍵字打頭的可選分支才顿,每一個分支都包含一個模式,如果模式匹配了尤蒿,模式對應(yīng)的表達(dá)式就會被求值郑气。一個match
表達(dá)式的求值過程是按照模式給出的順序逐一嘗試的。
- 模式匹配包含了一系列以
- 常量模式匹配常量腰池,變量模式可以匹配任何值尾组,
_
表示通用模式,并不會引入一個變量名來指向這個值示弓。
- 常量模式匹配常量腰池,變量模式可以匹配任何值尾组,
- 還有就是構(gòu)造者模式讳侨,相對于
if
和else
的判斷,非常非常的方便避乏。case _ =>
返回的是Unit
類型的()
值爷耀。
- 還有就是構(gòu)造者模式讳侨,相對于
模式的種類
- 通配模式:
_
用于任何對象,還可以用來忽略某個對象中并不關(guān)心的局部拍皮,在模式中使用名字歹叮,其實(shí)是將等效的對象或者變量綁定在這個名字上。
- 通配模式:
- 常量模式:僅能匹配自己铆帽。任何字面量都可以作為常量模式來使用咆耿。單例對象也可以被當(dāng)做常量模式使用。
val
對象也可以被當(dāng)做常量模式使用爹橱。
- 常量模式:僅能匹配自己铆帽。任何字面量都可以作為常量模式來使用咆耿。單例對象也可以被當(dāng)做常量模式使用。
-
- 變量模式:變量模式可以匹配任何對象萨螺,
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`
。
- 變量模式:變量模式可以匹配任何對象萨螺,
- 構(gòu)造方法模式:構(gòu)造方法模式是真正體現(xiàn)出模式匹配威力的地方今野。首先檢測被匹配的對象是否是該樣例類的實(shí)例晰奖,然后檢查該對象的構(gòu)造方法參數(shù)是否匹配模式中的構(gòu)造參數(shù)。構(gòu)造參數(shù)可能也是構(gòu)造模式腥泥,匹配時也對他們進(jìn)行檢測,因此匹配可以達(dá)到任意的深度啃匿。
- 序列模式:類似于樣例類匹配蛔外,可以跟序列類型做匹配。比如
List
或者Array
溯乒,使用的語法是相同的夹厌,但可以在模式中給出任意數(shù)量的元素,因?yàn)?code>List和Array
中含有接收任意參數(shù)的apply
工廠構(gòu)造方法裆悄。在該模式中使用_*
表示任意多個元素矛纹,如果要使用的話,必須放在構(gòu)造列表的最后面光稼。
- 序列模式:類似于樣例類匹配蛔外,可以跟序列類型做匹配。比如
- 元組模式或南,
(a,b,c)
可以匹配任意的三元組。
- 元組模式或南,
-
- 帶類型的模式:來代替類型檢測和類型轉(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]
。 - 這其中涉及到的一個問題是類型擦除冰垄,
Scala
和Java
一樣蹬癌,采用了擦除式的泛型,在運(yùn)行時并不會保留類型參數(shù)的信息虹茶,所以不能使用類似的語句來判斷逝薪,case m: Map[Int,Int] => m.size
,編譯器只能判斷某個變量是不是Map
蝴罪,而不能判斷這個Map
中key
和value
的類型董济。唯一例外的是數(shù)組,可以使用Array[String]
這樣的來進(jìn)行驗(yàn)證洲炊,數(shù)組的元素類型被保存了感局。(where???)
- 帶類型的模式:來代替類型檢測和類型轉(zhuǎn)換
- 變量綁定尼啡,除了各自存在的變量模式外,還可以對任何其他模式添加變量询微,在模式之前添加變量名以及一個
@
符號崖瞭,即可得到。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
的順序是很重要的。
密封類
- 當(dāng)編寫模式匹配的時候太闺,需要確保完整地覆蓋了所有可能的
case
糯景。為了使得Scala
編譯器能夠幫助我們檢測是否窮舉了所有的情況,需要阻止子類的不可控再增加省骂。手段是將這些樣例類的超類標(biāo)記為sealed
蟀淮,密封的。密封類除了在同一個文件中定義子類之外钞澳,不能添加新的子類灭贷。對于模式匹配非常有用,可以獲得更好的編譯器支持略贮。如果類被打算用于樣例類甚疟,一般會在類繼承關(guān)系的頂部類前加上sealed
關(guān)鍵字。
- 當(dāng)編寫模式匹配的時候太闺,需要確保完整地覆蓋了所有可能的
- 如果在
match
中可以明確確定只處理某些情況逃延,對缺失不檢查的情況可以使用@unchecked
注解览妖。在match
的選擇器部分進(jìn)行添加(e: @unchecked) match {}
,對分支的覆蓋完整性會被壓制揽祥。
- 如果在
Option類型
-
Option
表示可選值讽膏。將可選值解開的最常見方式是通過模式匹配,利用構(gòu)造器模式拄丰「鳎可以輕易的避免由null
帶來的空指針異常問題俐末。
Pattern is everywhere
- 在
Scala
中很多地方都可以使用模式。利用可以使用元組模式val (a, b) = (123, 234)
奄侠,在處理樣例類的時候非常有用卓箫,如果知道要處理的樣例類是什么,可以使用一個模式來析構(gòu)它垄潮。
- 在
- 作為偏函數(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)行時異常秋泳。
- 作為偏函數(shù)的
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ù)啤它。
-
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)用->
方法。