简介
模式可以当做对某个类型,其内部数据在结构上抽象出来的表达式。scala
中模式匹配使用match
关键字。match
可以当做是java
风格的switch
的广义化。但是有三个区别:
scala
中的match
是一个表达式,可以匹配各种情况;scala
的可选分支不会贯穿到下一个case
;- 如果一个模式都没匹配上,会抛出
MatchError
的异常。一般会添加什么都不做的缺省case
。
模式的种类
通配模式
通配模式(_)
可以匹配任何对象。1
2
3
4
5
6
7def judgeGrade(grade:String): Unit = {
grade match {
case _ => println("others")
}
}
judgeGrade("hello") //others
常量模式
常量模式仅匹配自己。任何字面量都可以作为常量模式使用。1
2
3
4
5
6
7
8
9
10
11def judgeGrade(grade:String): Unit = {
grade match {
case "A" => println("A")
case "B" => println("B")
case "C" => println("C")
case _ => println("others")
}
}
judgeGrade("A") //A
judgeGrade("B") //B
变量模式
变量模式匹配任何对象。这一点和通配模式相同。不同之处在于变量模式会将对应的变量绑定在匹配的对象上。之后可以用这个变量对对象作进一步的处理。1
2
3
4
5
6
7
8
9def matchSomething(something: Int): Unit = {
something match {
case 0 => println("zero")
case something => println("not zero: " + something)
}
}
matchSomething(0) // zero
matchSomething(1) // not zero: 1
上例中something
可以匹配任何除0
外的Int
值。
另外例子:1
2
3
4
5
6
7
8
9
10import math.{E, Pi}
def matchPi(x: Double): Unit = {
x match {
case Pi => println("Pi: " + Pi)
case _ => println("OK")
}
}
matchPi(E) // OK
可以看出E
并不匹配Pi
。scala
采用了一个简单的词法来区分:一个以小写字母打头的简单名称会被当做模式变量处理,所有其他引用都是常量。1
2
3
4
5
6
7
8
9
10import math.{E, Pi}
def matchpi(x: Double): Unit = {
x match {
case pi => println("Pi: " + pi)
case _ => println("OK")
}
}
matchPi(E) // Pi: 2.718281828459045
构造方法模式
如例所示。假设匹配的是一个样例类(case class
),这样模式将首先检查被匹配的对象是否是以这个名称命名的样例类的实例,然后再检查这个对象的改造方法参数是否匹配这些额外给出的模式。如果不是样例类,则需要定义伴生对象并实现unapply
方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16case class Person(name: String, age: Int)
object ConstructorPattern {
def main(args: Array[String]): Unit = {
val p = Person("stm", 25)
def constructorPattern(p: Person) = {
p match {
case Person(name, age) => println("name: " + name + ", age: " + age)
case _ => "other"
}
}
constructorPattern(p) // name: stm, age: 25
}
}
序列模式
可以和Array
、List
等序列类型匹配。在模式中可以给出任意数量的元素。其原理也是通过case class
。_*
可以匹配剩余元素,包括0个。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15object SequencePattern {
def main(args: Array[String]): Unit = {
val list = List("spark", "hive")
val arr = Array("scala", "java", "python")
def sequencePattern(p: Any) = p match {
case List(_, second, _*) => println(second)
case Array(first, second, _*) => println(first + ", " + second)
case _ => println("other")
}
sequencePattern(list) // hive
sequencePattern(arr) // scala, java
}
}
元组模式
元组模式用于匹配scala
中的元组内容。_*
不适用于元组。1
2
3
4
5
6
7
8
9
10
11
12
13
14object TuplePattern {
def main(args: Array[String]): Unit = {
val tuple1 = ("spark", "hive", "hadoop")
val tuple2 =("java", "python")
def tuplePattern(t:Any) = t match {
case (one, _, _) => println(one)
case (one, two) => println(one + ", " + two)
case _ => println("other")
}
tuplePattern(tuple1) // spark
tuplePattern(tuple2) // java, python
}
}
类型模式
可以用来替代类型测试和类型转换。Map[_, _]
匹配任意Map
。1
2
3
4
5
6
7
8
9
10
11
12object TypePattern {
def main(args: Array[String]): Unit = {
def typePattern(t:Any) = t match {
case t :String => println("t.length: " + t.length)
case t :Map[_, _] => println("t.size: " + t.size)
case _ => println("other")
}
typePattern("hello") // t.length: 5
typePattern(Map(1->'a', 2->'b')) // t.size: 2
}
}
类型擦除
java
和scala
中都采用了擦除式的泛型。即在运行中无法判定某个给定的Map对象是用两个Int
类型参数创建还是其他类型。但是数组除外。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21object TypeErasure {
def main(args: Array[String]): Unit = {
val m1 = Map(1 -> 1, 2 -> 2)
val m2 = Map(1 -> "a", 2-> "b")
def isIntIntMap(x:Any) = x match {
case m:Map[Int, Int] => println(true)
case _ => println(false)
}
isIntIntMap(m1) //true
isIntIntMap(m2) //true
def isStringArray(x:Any) = x match {
case x : Array[String] => println(true)
case _ => println(false)
}
isStringArray(Array("hello")) //true
isStringArray(Array(33)) //false
}
}
变量绑定模式
可以对任何其他模式添加变量。只需要写下变量名、一个@符合模式本身,就得到一个变量绑定模式。如果匹配成功,就将匹配的对象赋值给这个变量。1
2
3
4
5
6
7
8
9
10
11
12object VariableBindingPattern {
def main(args: Array[String]): Unit = {
val t = List(List(1,2), List(4,5,6))
def variableBindingPattern(t:Any) = t match {
case List(_, e (_, _, _)) => println(e)
case _ => println("other")
}
variableBindingPattern(t) //List(4, 5, 6)
}
}
模式守卫
使用if
表达式。1
2
3
4
5
6
7
8
9
10
11
12
13
14object PatternGuards {
def main(args: Array[String]): Unit = {
val list1 = List(1, 2, 3)
val list2 = List(4, 5, 6)
def patternGuards(x: Any) = x match {
case List(first, _*) if first == 1 => println(x)
case _ => println("others")
}
patternGuards(list1) //List(1, 2, 3)
patternGuards(list2) //others
}
}