1.语法
选择器 match {
可选分支1: case 模式类型 => {表达式}
可选分支2: case 模式类型 => {表达式}
}
模式匹配包含一系列以case关键字打头的可选分支(alternative)
每一个可选分支都包括一个模式(pattern)以及一个或多个表达式
用箭头符=>用于将模式和表达式分开
如果模式匹配了,这些表达式就会被求值
一个match表达式的求值过程是按照模式给出的顺序逐一尝试的
第一个匹配上的模式被选中,跟在这个模式后面的表达式被执行
2.模式的种类
2.1 通配模式(wildcard pattern)
功能:
1.用 _ 来匹配任何对象
2.通配模式还可以用来忽略某个对象中你并不关心的局部
test("通配模式") {
case class MatchClass(id: Int, name: String, age: Int)
def matchVal(x: Any) = x match {
case MatchClass(10, "小明", 99) => println(x)
case MatchClass(99, _, _) => println(x) // 通配模式还可以用来忽略某个对象中你并不关心的局部
case _ => println("It's something else") // 用 _ 来匹配任何对象
}
matchVal(99)
matchVal(MatchClass(10, "小明", 99))
matchVal(MatchClass(99, "小红", 100))
}
2.2 常量模式(constant pattern)
功能:
匹配任何类型的字面量
单例对象也可以作为常量模式使用
Nil表示 空集合单例对象
test("常量模式") {
object MatchClass
def matchVal(x: Any) = x match {
case 5 => "five"
case true => "true"
case "hello" => "hi!"
case MatchClass => "MatchClass"
case Nil => "the empty list!" // Nil 表示 空列表对象
case _ => "something else"
}
println(matchVal(5))
println(matchVal(true))
println(matchVal("hello"))
println(matchVal(MatchClass))
println(matchVal(List()))
println(matchVal(List(1, 2, 3)))
}
2.3 变量模式(variable pattern)
功能:
用变量名称 来匹配任何对象(和 通配模式类似)
和通配模式不同之处是:
Scala将匹配的对象 会绑定在变量上,并可以在 表达式中使用变量
test("变量模式") {
def matchVal(x: Any) = x match {
case 0 => "zero"
case somethingElse => "not zero: " + somethingElse
}
println(matchVal(5))
println(matchVal("小明"))
}
2.4 构造方法模式(constructor pattern)
说明:
构造方法模式是真正体现出模式匹配威力的地方
使用前提:
构造方法所属的类必须是 样例类或者样例类的子类
功能:
首先 检查匹配的对象是否为 指定模式中样例类或者样例类子类的实例
再检查 匹配对象构造方法参数 是否匹配模式中构造器的参数
通配符的使用:
1. 当构造器的参数不确定时,可以用 _来替代参数
2. 样例类名称 = 样例类名称(_,_,_)
test("构造方法模式") {
case class cPerson(id: Int, name: String, age: Int)
class nPerson(id: Int, name: String, age: Int)
class scPerson(id: Int, name: String, age: Int) extends cPerson(id, name, age) {
override def toString: String = s"scPerson:${id}、${name}、${age}"
}
def matchConstructor(x: Any) = x match {
case cPerson(1, "张飞", 88) => println("0: " + x)
case cPerson => println("1: " + x) // cPerson 等价 cPerson(_,_,_)
case cPerson(_, _, _) => println("2: " + x) // cPerson 等价 cPerson(_,_,_)
//case nPerson(_,_,_) => println("3: "+x)
case _ => println("else: " + x)
}
matchConstructor(cPerson(1, "大王", 90))
matchConstructor(cPerson(1, "张飞", 88))
// 样例类子类的实例
matchConstructor(new scPerson(1, "张飞", 88))
}
2.5 序列模式(list pattern)
功能:
用来匹配 List或Array的实例
通配符的使用:
_ : 单个元素占位符
_* : 任意个数元素占位符
test("序列模式") {
def matchList(x: Any) = x match {
case List(1, 2, 3) => println("case1: " + x) // 精准匹配
case List(_, _, _) => println("case2: " + x) // 按 list 长度匹配
case List(0, _*) => println("case3: " + x) // 按 list 开头元素匹配
case _ => println("else: " + x)
}
matchList(List(1, 2, 3))
matchList(List(9, 9, 9))
matchList(List(0, 1, 2, 3))
}
2.6 元组模式(tuple pattern)
功能:
用来匹配元素的实例
通配符的使用:
_ : 单个元素占位符
_* : 任意个数元素占位符 (不支持)
test("元组模式") {
def matchTuple(x: Any) = x match {
case (1, 2, 3) => println("case1: " + x) // 精准匹配
case (_, _, _) => println("case2: " + x) // 匹配 元素任意的三元组
//case (0, _*) => println("case3: " + x) // 不可以使用 _*,来匹配任意数量的元组
case _ => println("else: " + x)
}
matchTuple((1, 2, 3))
matchTuple((9, 9, 9))
matchTuple((0, 1, 2, 3))
}
2.7 带类型的模式(typed pattern)
功能:
用来匹配参数的数据类型
使用场景:
可以用来做参数的数据类型测试和类型转换
尤其是 同时对参数 做类型测试和类型转换时,尤为好使
注意:
当类型为 集合类型时,并不会判断集合元素的数据类型(泛型擦除)
test("带类型的模式") {
/*
* 需求描述:
* 判断参数数据类型,并返回参数长度
*
* */
// 使用 match表达式
def generalSize(x: Any) = x match {
case s: String => s.length
case m: Map[_, _] => m.size
case l: List[String] => l.length
case a: Array[t] => a.length
case _ => -1
}
/*
* x 和 s 的区别:
* 经过 x和s都指向同一个值,但是 x的类型是Any,s的类型是String
* s做参数绑定时,已经将数据类型转换成了String
*
* */
println(generalSize("abc"))
println(generalSize(Map("x" -> 10, "y" -> 100)))
println(generalSize(List(1, 2, 3, 4, 5)))
println(generalSize(List("x", "y", "z")))
}
2.7.1 对比 isInstanceOf 和 asInstanceOf 和 带类型的模式的区别
obj.isInstanceOf[String] :
判断 变量 数据类型是否为 String
obj.asInstanceOf[String] :
对 变量 进行强制类型转换
test("带类型的模式") {
// 使用 isInstanceOf 和 asInstanceOf 实现
def generalSizeUseIf(x: Any): Int = {
if (x.isInstanceOf[String]) {
val s = x.asInstanceOf[String]
s.length
} else if (x.isInstanceOf[Map[_, _]]) {
val m = x.asInstanceOf[Map[_, _]]
m.size
} else {
-1
}
}
println(generalSizeUseIf("abc"))
println(generalSizeUseIf(Map("x" -> 10, "y" -> 100)))
println(generalSizeUseIf(List(1, 2, 3, 4, 5)))
println(generalSizeUseIf(List("x", "y", "z")))
}
在做类型测试和转化时,isInstanceOf 和 asInstanceOf 和 模式匹配都可以实现
但是 上面这种实现方式 代码更加冗长和啰嗦,所以建议使用 模式匹配
3. 什么是类型擦除(泛型擦除) ???
Scala采用了`擦除式泛型`,和java实现一样
也就是说,泛型只作用在编译阶段,在运行时并不会保留泛型的信息
在 match表达式 的模式中
m: Map[Int, Int] 和 m: Map[_, _] 和 m: Map[a, b] 是等价的
对于这个泛型擦除规则唯一例外的是数组,因为java和scala都对它们做了特殊处理
数组的元素类型和数组是一起保存的,所有可以对数组的泛型做模式匹配
test("泛型擦除") {
def isIntIntMap(x: Any) = x match {
case m1: Map[Int, Int] => 1
case m2: Map[_, _] => 2
case m3: Map[a, b] => 3
case _ => 4
}
println(isIntIntMap(Map(1 -> 1)))
println(isIntIntMap(Map("x" -> "y")))
def isStringArray(x: Any) = x match {
case s1: Array[String] => 1
case s2: Array[Int] => 2
case _ => 3
}
println(isStringArray(Array("abc")))
println(isStringArray(Array(123)))
println(isStringArray(Array(13.1)))
}
4. 怎样在模式中变量绑定
功能:
除了 变量模式和带类型的模式 会将匹配的对象 绑定在模式中指定变量上
其他模式中 也可以使用变量绑定
语法:
变量名称 @ 模式本身
test("变量绑定") {
case class cPerson(id: Int, name: String, age: Int)
def matchVal(x: Any) = x match {
case cPerson(id@_, name@_, age@_) => println(s"${id}、${name}、${age}")
case _ => println("else case")
}
matchVal(cPerson(10, "大王", 99))
}
5. 什么是模式守卫
模式守卫 是放在模式后面的 一段判断逻辑(if + 任意的布尔表达式)
当 模式守卫为true时 模式匹配成功,否则为匹配失败
通常 模式守卫 会引用模式中的变量,用来增强模式匹配的判断能力
语法:
选择器 match {
可选分支1: case 模式类型 模式守卫 => {表达式}
可选分支2: case 模式类型 模式守卫 => {表达式}
}
模式守卫 : if + 布尔表达式
test("模式守卫") {
case class cPerson(id: Int, name: String, age: Int)
def matchVal(x: Any) = x match {
// 匹配 age>0 的实例
case cPerson(id@_, name@_, age@_) if age > 0 => println(s"${id}、${name}、${age}")
// 匹配 长度>5 的字符串
case s: String if s.length > 5 => println(s)
case _ => println("else case")
}
matchVal(cPerson(10, "大王", 99))
matchVal(cPerson(10, "大王", 0))
matchVal("abcd")
matchVal("abcdef")
}
6. 什么是模式叠加
match表达式中 会按照 可选分支的顺序进行匹配
如果当前分支的匹配范围 小于上部分支的匹配范围,那么会造成当前分支是无效的(部分编译器 发出告警)
test("模式叠加") {
case class cPerson(id: Int, name: String, age: Int)
def matchOverlay(x: Any) = x match {
case cPerson => println("case1")
case cPerson(10, "dawang", 20) => println("case2") // 这个分支是无效的
case _ => println("case3")
}
matchOverlay(cPerson(10, "dawang", 20))
matchOverlay(cPerson(0, "xiaowang", 88))
}
7. Scala中 match和 java中switch的区别
Scala中match是一个表达式(总能得到一个值)
Scala的可选分支不会贯穿(full through)到下一个case
Scala中如果一个模式也没匹配上,会抛出MatchError的异常