知识点
- mathch表达式是一个更好的switch,不会有穿透到下一个分支的问题
- 如果没有模式能够匹配,会抛出MatchError,可以用case _ 模式来避免,相当于Java中的default
- 模式可以包含一个随意定义的条件,称做守卫
- 可以匹配数组、列表、元组等模式,然后将匹配到不同部分绑定到变量
- 样例类及密封类的模式匹配
- 用Option来存放可能存在也可能不存在的值,比null更安全
更好的switch(match case)
- 与if类似,match也是表达式,而不是语句
object MatchLearn extends App { val ch: Char = '-' val sign = ch match { case '+' => 1 case '-' => -1 case _ => 0 } println("sign " + sign) } // 输出 sign -1
- 与Java的switch不同,Scala模式匹配并不会自动进行到下一个分支,不必在每个分支末尾显示地使用
break
语句 - 与Java的default等效的
case_
模式,有这样一个捕获所有模式的方式,如果没有模式匹配到,代码会抛出MatchError - 在match表达式中使用任意类型,而不仅仅是数字
import java.awt.Color /** * @author Yezhiwei * @date 18/1/8 */ object MatchLearn extends App { // match 还可以是非数字类型的 val color = Color.RED val c = color match { case Color.RED => "red" case Color.BLACK => "black" case _ => "pink" } println("color is " + c) }
- 在Scala中,可以给模式添加守卫,如匹配所有数字
object MatchLearn extends App { val param = '4' val matchResult = param match { case '+' => 1 case '-' => -1 case _ if Character.isDigit(param) => Character.digit(param, 10) case _ => 0 } println("match result " + matchResult) }
说明:模式总是从上往下进行匹配的,如果带守卫的这个模式不能匹配,则捕获所有的模式(case _)会被用来尝试进行匹配。
- 如果
case
关键字后面跟着一个变量名,那么匹配的表达式会被赋值给那个变量,还可以在守卫中使用变量
object MatchLearn extends App { ... val param1 = '4' val matchResult1 = param match { case '+' => 1 case '-' => -1 case ch if Character.isDigit(ch) => Character.digit(ch, 10) case _ => 0 } println("match result1 : " + matchResult1) }
类型模式
- 可以对表达式的类型进行匹配
object MatchLearn extends App { ... def patternType(obj: Any) = obj match { case x: Int => x case s: String => s case _: BigInt => Int.MaxValue case _ => 0 } println(patternType(1)) println(patternType("1")) println(patternType(BigInt.apply(10))) println(patternType(BigDecimal.apply(10))) } // 输出结果: 1 1 2147483647 0
- 匹配发生在运行期,JVM中泛型的类型信息会被擦掉,因此,不能用类型来匹配特定的Map类型
casem:Map[String,Int]=>...// no
可以用casem:Map[_,_]=>...// ok
- 在Scala中,更倾向于使用模式匹配,而不是isInstanceOf操作符
匹配数组、列表和元组
- 要匹配数组的内容,可以在模式中使用Array表达式
object MatchLearn extends App { ... def patternArray(obj: Array[Any]) = obj match { // 匹配包含0的数组 case Array(0) => "0" // 匹配任意两个元素的数组,并将这两个元素分别绑定到x 和 y 变量 case Array(x, y) => x + " <-> " + y // 匹配以0开始的数组 case Array(0, _*) => "0 ..." case _ => "something else" } println(patternArray(Array(0))) println(patternArray(Array(1, 2))) println(patternArray(Array(0, 2, 4))) println(patternArray(Array(2, 4, 6))) }
说明:
匹配包含0的数组
匹配任意两个元素的数组,并将这两个元素分别绑定到x 和 y 变量
匹配以0开始的数组
- 使用List表达式,匹配列表,或者可以使用
::
操作符
1. object MatchLearn extends App { 2. 3. ... 4. def patternList(obj: List[Int]) = obj match { 5. // 匹配包含0的List 6. case 0 :: Nil => "0" 7. // 匹配任意两个元素的List,并将这两个元素分别绑定到x 和 y 变量 8. case x :: y :: Nil => x + " <-> " + y 9. // 匹配以0开始的List 10. case 0 :: tail => "0 ..." 11. case _ => "something else" 12. } 13. 14. println(patternList(List(0))) 15. println(patternList(List(1, 2))) 16. println(patternList(List(0, 2, 4, 6, 8))) 17. println(patternList(List(2, 4, 6, 8))) 18. } • 元组在模式匹配中使用 19. object MatchLearn extends App { 20. 21. ... 22. print("*" * 10) 23. print("Tuple") 24. println("*" * 10) 25. def patternTuple(x: Int, y: Int) = (x, y) match { 26. // 匹配包含0的List 27. case (0, _) => "0 ... " 28. // 匹配任意两个元素的List,并将这两个元素分别绑定到x 和 y 变量 29. case (y, 0) => y + " 0 " 30. case _ => "tuple something else" 31. } 32. 33. println(patternTuple(0, 1)) 34. println(patternTuple(2, 0)) 35. println(patternTuple(1, 2)) 36. } 37. 38. // 输出 39. 0 ... 40. 2 0 41. tuple something else
样例类
- 样例类是一种特殊类,它们常用于模式匹配
object CaseClassLearn extends App { def patternAmount(amount: Amount) = amount match { case Dollar(v) => "$" + v case Currency(v, u) => "I got " + v + " " + u case Nothing => "Nothing" case _ => "" } println(patternAmount(Dollar(1.0))) println(patternAmount(Currency(1.0, "RMB"))) println(patternAmount(Nothing)) } abstract class Amount case class Dollar(value: Double) extends Amount case class Currency(value: Double, unit: String) extends Amount // 样例对象 case object Nothing extends Amount // 输出 $1.0 I got 1.0 RMB Nothing
说明:
样例类的实例使用(), 样例对象不使用圆括号()
声明样例类时,构造器中的每一个参数都成为val
在伴生对象中提供apply方法,让你不用new关键字就能构造出相应的对象,如:
Dollar(1.0)
提供unapply方法让模式匹配可以工作
将生成
toString,equals,hashCode,copy
方法
密封类(sealed样例类)
- 当用样例类来做模式匹配时,你可能想让编译器帮你确保你已经列了所有的可能选择,要达到这个目的,需要将样例类的通用超类声明为
sealed
- 密封类的所有子类都必须在与该密封类相同的文件中定义
- 如果某个类是密封的,那么在编译期所有子类就是可知的,因而编译器可以检查模式语句的完整性,让同一组样例类都扩展某个密封的类或特质是个好的做法
在上面示例的基本上增加一下
sealed
abstract class Amount,然后注释掉一个 case 重新编译试一试
Option类型
- 标准类库中的Option类型用样例类来表示可能存在、也可能不存在的值
- 子类Some包装了某个值,如
Some("Fred")
,None表示没有值,比使用空字符串的意图列加清晰,比使用null来表示缺少的某值的做法更加安全 - 也可以通过模式匹配来输出匹配值
object OptionLearn extends App { val site: Map[String, String] = Map("baidu" -> "www.baidu.com", "google" -> "www.google.com") val value1 = site.get("Yezhiwei") val value2: Option[String] = site.get("Yezhiwei") val value3: Option[String] = site.get("baidu") println("value1 " + value1) println("value2 " + value2) println("value3 " + value3) println() println("show value2 " + show(value2)) println("show value3 " + show(value3)) def show(v: Option[String]) = v match { case Some(v) => v case None => "?" } } // 输出 value1 None value2 None value3 Some(www.baidu.com) show value2 ? show value3 www.baidu.com