Scala快速入门-10-模式匹配与样例类

picture.image

知识点

  • mathch表达式是一个更好的switch,不会有穿透到下一个分支的问题
  • 如果没有模式能够匹配,会抛出MatchError,可以用case _ 模式来避免,相当于Java中的default
  • 模式可以包含一个随意定义的条件,称做守卫
  • 可以匹配数组、列表、元组等模式,然后将匹配到不同部分绑定到变量
  • 样例类及密封类的模式匹配
  • 用Option来存放可能存在也可能不存在的值,比null更安全

更好的switch(match case)

  • 与if类似,match也是表达式,而不是语句

 1. `object MatchLearn  extends App {`
2. 
3. `val ch: Char = '-'`
4. 
5. `val sign = ch match {`
6. `case '+' => 1`
7. `case '-' => -1`
8. `case _ => 0`
9. `}`
10. 
11. `println("sign " + sign)`
12. `}`
13. 
14. `// 输出`
15. `sign -1`

  • 与Java的switch不同,Scala模式匹配并不会自动进行到下一个分支,不必在每个分支末尾显示地使用 break语句
  • 与Java的default等效的 case\_模式,有这样一个捕获所有模式的方式,如果没有模式匹配到,代码会抛出MatchError
  • 在match表达式中使用任意类型,而不仅仅是数字

 1. `import java.awt.Color`
2. 
3. `/**`
4. `* @author Yezhiwei`
5. `* @date 18/1/8`
6. `*/`
7. `object MatchLearn  extends App {`
8. 
9. `// match 还可以是非数字类型的`
10. `val color = Color.RED`
11. `val c = color match {`
12. `case Color.RED => "red"`
13. `case Color.BLACK => "black"`
14. `case _ => "pink"`
15. `}`
16. 
17. `println("color is " + c)`
18. `}`

  • 在Scala中,可以给模式添加守卫,如匹配所有数字

 1. `object MatchLearn  extends App {`
2. 
3. `val param = '4'`
4. 
5. `val matchResult = param match {`
6. `case '+' => 1`
7. `case '-' => -1`
8. `case _ if Character.isDigit(param) => Character.digit(param, 10)`
9. `case _ => 0`
10. `}`
11. 
12. `println("match result " + matchResult)`
13. `}`

说明:模式总是从上往下进行匹配的,如果带守卫的这个模式不能匹配,则捕获所有的模式(case _)会被用来尝试进行匹配。

  • 如果 case关键字后面跟着一个变量名,那么匹配的表达式会被赋值给那个变量,还可以在守卫中使用变量

 1. `object MatchLearn  extends App {`
2. 
3. `...`
4. 
5. `val param1 = '4'`
6. 
7. `val matchResult1 = param match {`
8. `case '+' => 1`
9. `case '-' => -1`
10. `case ch if Character.isDigit(ch) => Character.digit(ch, 10)`
11. `case _ => 0`
12. `}`
13. 
14. `println("match result1 : " + matchResult1)`
15. `}`

类型模式

  • 可以对表达式的类型进行匹配

 1. `object MatchLearn  extends App {`
2. 
3. `...`
4. 
5. `def patternType(obj: Any) = obj match {`
6. `case x: Int => x`
7. `case s: String => s`
8. `case _: BigInt => Int.MaxValue`
9. `case _ => 0`
10. `}`
11. 
12. `println(patternType(1))`
13. `println(patternType("1"))`
14. `println(patternType(BigInt.apply(10)))`
15. `println(patternType(BigDecimal.apply(10)))`
16. `}`
17. 
18. `// 输出结果:`
19. `1`
20. `1`
21. `2147483647`
22. `0`

  • 匹配发生在运行期,JVM中泛型的类型信息会被擦掉,因此,不能用类型来匹配特定的Map类型 casem:Map[String,Int]=>...// no 可以用 casem:Map[\_,\_]=>...// ok
  • 在Scala中,更倾向于使用模式匹配,而不是isInstanceOf操作符

匹配数组、列表和元组

  • 要匹配数组的内容,可以在模式中使用Array表达式

 1. `object MatchLearn  extends App {`
2. 
3. `...`
4. `def patternArray(obj: Array[Any]) = obj match {`
5. `// 匹配包含0的数组`
6. `case Array(0) => "0"`
7. `// 匹配任意两个元素的数组,并将这两个元素分别绑定到x 和 y 变量`
8. `case Array(x, y) => x + " <-> " + y`
9. `// 匹配以0开始的数组`
10. `case Array(0, _*) => "0 ..."`
11. `case _ => "something else"`
12. `}`
13. 
14. `println(patternArray(Array(0)))`
15. `println(patternArray(Array(1, 2)))`
16. `println(patternArray(Array(0, 2, 4)))`
17. `println(patternArray(Array(2, 4, 6)))`
18. `}`

说明:

匹配包含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. `}`

  • 元组在模式匹配中使用

 1. `object MatchLearn  extends App {`
2. 
3. `...`
4. `print("*" * 10)`
5. `print("Tuple")`
6. `println("*" * 10)`
7. `def patternTuple(x: Int, y: Int) = (x, y) match {`
8. `// 匹配包含0的List`
9. `case (0, _) => "0 ... "`
10. `// 匹配任意两个元素的List,并将这两个元素分别绑定到x 和 y 变量`
11. `case (y, 0) => y + " 0 "`
12. `case _ => "tuple something else"`
13. `}`
14. 
15. `println(patternTuple(0, 1))`
16. `println(patternTuple(2, 0))`
17. `println(patternTuple(1, 2))`
18. `}`
19. 
20. `// 输出`
21. `0 ...`
22. `2 0`
23. `tuple something else`

样例类

  • 样例类是一种特殊类,它们常用于模式匹配

 1. `object CaseClassLearn extends App {`
2. 
3. 
4. `def patternAmount(amount: Amount) = amount match {`
5. `case Dollar(v) => "$" + v`
6. `case Currency(v, u) => "I got " + v + " " + u`
7. `case Nothing => "Nothing"`
8. `case _ => ""`
9. `}`
10. 
11. 
12. `println(patternAmount(Dollar(1.0)))`
13. `println(patternAmount(Currency(1.0, "RMB")))`
14. `println(patternAmount(Nothing))`
15. `}`
16. 
17. `abstract class Amount`
18. 
19. `case class Dollar(value: Double) extends Amount`
20. 
21. `case class Currency(value: Double, unit: String) extends Amount`
22. `// 样例对象`
23. `case object Nothing extends Amount`
24. 
25. `// 输出`
26. 
27. `$1.0`
28. `I got 1.0 RMB`
29. `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来表示缺少的某值的做法更加安全
  • 也可以通过模式匹配来输出匹配值

 1. `object OptionLearn extends App {`
2. 
3. `val site: Map[String, String] = Map("baidu" -> "www.baidu.com", "google" -> "www.google.com")`
4. `val value1 = site.get("Yezhiwei")`
5. `val value2: Option[String] = site.get("Yezhiwei")`
6. `val value3: Option[String] = site.get("baidu")`
7. 
8. `println("value1 " + value1)`
9. `println("value2 " + value2)`
10. `println("value3 " + value3)`
11. `println()`
12. `println("show value2 " + show(value2))`
13. `println("show value3 " + show(value3))`
14. 
15. 
16. `def show(v: Option[String]) = v match {`
17. 
18. `case Some(v) => v`
19. `case None => "?"`
20. `}`
21. `}`
22. 
23. `// 输出`
24. `value1 None`
25. `value2 None`
26. `value3 Some(www.baidu.com)`
27. 
28. `show value2 ?`
29. `show value3 www.baidu.com`


敬请期待下一篇<常用集合操作>~

picture.image

0
0
0
0
评论
未登录
暂无评论