Scala之模式匹配

基本介绍
Scala 中的模式匹配相似于 Java 中的 switch 语法,可是更增强大。
模式匹配语法中,采用 match 关键字声明,每一个分支采用 case 关键字进行声明,当须要匹配时,
会从第一个 case 分支开始,若是匹配成功,那么执行对应的逻辑代码,若是匹配不成功,继续执行下
一个分支进行判断。若是全部 case 都不匹配,那么会执行 case _ 分支,相似于 Java 中 default 语句。

match 的细节和注意事项
1) 若是全部 case 都不匹配,那么会执行 case _ 分支,相似于 Java 中 default 语句
2) 若是全部 case 都不匹配,又没有写 case _ 分支,那么会抛出 MatchError
3) 每一个 case 中,不用 break 语句,自动中断 case
4) 能够在 match 中使用其它类型,而不单单是字符
5) => 等价于 java swtich 的 :
6) => 后面的代码块到下一个 case, 是做为一个总体执行,可使用{} 扩起来,也能够不扩。

object MatchDemo01 {
  def main(args: Array[String]): Unit = {
    val oper = '-'
    val n1 = 20
    val n2 = 10
    var res = 0
    // 说明
    //1. match ( 相似 java switch)  和 case  是关键字
    //2. , 若是匹配成功, 则 则 行 执行 =>  后面的代码块.
    //3. 的 匹配的顺序是从上到下,匹配到一个就执行对应的  代码
    //4. => 块 后面的代码块 写 不要写 break , 会自动的退出 match
    //5. 行 若是一个都没有匹配到,则执行 case _  后面的代码块
      oper match {
      case '+' => res = n1 + n2
      case '-' => {
        println("也能够大括号使用,写多行代码")
        res = n1 - n2
      }
      case '*' => res = n1 * n2
      case '/' => res = n1 / n2
      case _ => println("oper error")
    }
    println("res=" + res)
  }
}

守卫:若是想要表达匹配某个范围的数据,就须要在模式匹配中增长条件守卫

object MatchIfDemo01 {
  def main(args: Array[String]): Unit = {
    for (ch <- "+-3!") { //是对"+-3!" 遍历
      var sign = 0
      var digit = 0
      ch match {
        case '+' if ch.toString.equals("3") => sign = 1
        case '-' => sign = -1
        // 说明..
        // 若是 case 后有 条件守卫即 if ,那么这时的 _ 不是表示默认匹配
        // 表示忽略 传入 的 ch
        case _ if ch.toString.equals("3") => digit = 3
        case _ if (ch > 1110 || ch < 120) => println("ch > 10")
        case _ => sign = 2
      }
      //分析
      // + 1 0
      // - -1 0
      // 3 0 3
      // ! 2 0
      println(ch + " " + sign + " " + digit)
    }
  }
}

模式中的变量:若是在 case 关键字后跟变量名,那么 match 前表达式的值会赋给那个变量

object MatchVar {
  def main(args: Array[String]): Unit = {
    val ch = 'U'
    ch match {
      case '+' => println("ok~")
      // 下面 case mychar 含义是 mychar = ch   下面这个case就啥均可以匹配上.这样很差
      case mychar => println("ok~" + mychar)
      case _ => println ("ok~~")
    }
    val ch1 = '+'
    //match 是一个表达式,所以能够有返回值
    //返回值就是匹配到的代码块的最后一句话的值
    val res = ch1 match {
      case '+' => {
        print("使用这种方式,case里的最后一行就会返回结果给变量res,这种方式好,")
        ch1 + " hello "
      }
      // 下面 case mychar 含义是 mychar = ch
      case _ => println ("ok~~")
    }
    println("res=" + res)
  }
}

 类型匹配:能够匹配 对象的任意类型,这样作避免了使用 isInstanceOf 和 asInstanceOf 方法

类型匹配注意事项
1) Map[String, Int] 和 Map[Int, String]是两种不一样的类型,其它类推。
2) 在进行类型匹配时,编译器会预先检测是否有可能的匹配,若是没有则报错.

3) 若是 case _ 出如今 match 中间,则表示隐藏变量名,即不使用,而不是表示默认匹配

object MatchTypeDemo01 {
  def main(args: Array[String]): Unit = {
    val a = 1
    //说明 obj 实例的类型 根据 a 的值来返回
    val obj = if (a == 1) 1
    else if (a == 2) "2"
    else if (a == 3) BigInt(3)
    else if (a == 4) Map("aa" -> 1)
    else if (a == 5) Map(1 -> "aa")
    else if (a == 6)Array(1, 2, 3)
    else if (a == 7)Array("aa", 1)
    else if (a == 8)Array("aa")
    //说明
    //1. 根据 obj 的类型来匹配
    // 返回值
    val result = obj match {
      case a: Int => a
      case b: Map[String, Int] => "对象是一个字符串-数字的 Map 集合"
      case c: Map[Int, String] => "对象是一个数字-字符串的 Map 集合"
      case d: Array[String] => d //"对象是一个字符串数组"
      case e: Array[Int] => "对象是一个数字数组"
      case f: BigInt => Int.MaxValue
      case _ => "啥也不是"
    }
    println(result)
  }
}

匹配数组

基本介绍
1) Array(0) 匹配只有一个元素且为 0 的数组。
2) Array(x,y) 匹配数组有两个元素,并将两个元素赋值为 x 和 y。固然能够依次类推 Array(x,y,z) 匹
配数组有 3 个元素的等等....
3) Array(0,_*) 匹配数组以 0 开始

object MatchArr {
  def main(args: Array[String]): Unit = {

     val arrs =Array(Array(0), Array(1, 0), Array(0, 1, 0),
     Array(1, 1, 0), Array(1, 1, 0, 1))

     for (arr <- arrs ) {
     val result = arr match {
     case Array(0) => "0"
     case Array(x, y) => x + "=" + y
     case Array(0, _*) => "以 0 开头和数组"
     case _ => "什么集合都不是"
     }
     // result = 0
     // result = 1 = 0
     // result = 以 0 开头和数组
     // result = 什么集合都不是
     // result = 什么集合都不是
     println("result = " + result)
     }
    //给你一个数组集合,若是该数组时 Array(10,20) , 请使用默认匹配,返回 Array(20,10)
    import scala.collection.mutable.ArrayBuffer
    val arrs2 = Array(Array(0),Array(1, 0), Array(0, 1, 0),
      Array(1, 1, 0), Array(1, 1, 0, 1))
    for (arr <- arrs2 ) {
      val result = arr match {
        //caseArray(0) => "0"
        case Array(x,y)=>ArrayBuffer(y,x) //?ArrayB(y,x)
        //caseArray(0, _*) => "以 0 开头和数组"
        case _ => "不处理~~"
      }
      println(result)
    }
  }
}

匹配列表

object MatchList {
  def main(args: Array[String]): Unit = {
    for (list <-Array(List(0), List(1, 0), List(88), List(0, 0, 0), List(1, 0, 0))) {
      val result = list match {
        case 0 :: Nil => "0" //
        case x :: y :: Nil => x + " " + y //
        case 0 :: tail => "0 ..." //
        case x :: Nil => x
        case _ => "something else"
      }
      //1. 0
      //2. 1 0
      //3. 0 ...
      //4. something else
      println(result)
    }
  }
}

匹配元组

object MatchTupleDemo01 {
  def main(args: Array[String]): Unit = {
    //若是要匹配 (10, 30) 这样任意两个元素的对偶元组,应该如何写
    for (pair <-Array((0, 1), (1, 0), (10, 30), (1, 1), (1, 0, 2))) {
      val result = pair match { //
        case (0, _) => "0 ..." //
        case (y, 0) => y //
        case (x, y) => (y, x) //"匹配到(x,y)" + x + " " + y
        case _ => "other" //.
      }
      //1. 0 ...
      //2. 1
      //3. other
      //4. other
      println(result)
    }
  }
}

对象匹配

基本介绍
对象匹配,什么才算是匹配呢?,规则以下:
1) case 中对象的 unapply 方法(对象提取器)返回 Some 集合则为匹配成功
2) 返回 None 集合则为匹配失败

object MatchObject {
  def main(args: Array[String]): Unit = {
    // 模式匹配使用:
    val number: Double = Square(5.0)// 36.0 //
    number match {
      //说明 case Square(n) 的运行的机制
      //1. 当匹配到 case Square(n)
      //2. 调用 Square 的 unapply(z: Double),z 的值就是 number
      //3. 若是对象提取器 unapply(z: Double) 返回的是 Some(6) ,是some对象则表示匹配成功,同时
      // 将 6 赋给 Square(n) 的 n
      //4. 果对象提取器 unapply(z: Double) 返回的是 None ,则表示匹配不成功
      case Square(n) => println("匹配成功 n=" + n)
      case _ => println("nothing matched")
    }
  }
}
object Square {
  //说明
  //1. unapply 方法是对象提取器
  //2. 接收 z:Double 类型
  //3. 返回类型是 Option[Double]
  //4. 返回的值是 Some(math.sqrt(z)) 返回 z 的开平方的值,并放入到 Some(x)
  def unapply(z: Double): Option[Double] = {
      println("unapply 被调用 z 是=" + z)
      Some(math.sqrt(z))
   // None
  }
  def apply(z: Double): Double = z * z
}

案例二

1) 当 case 后面的对象提取器方法的参数为多个,则会默认调用 def unapplySeq() 方法
2) 若是 unapplySeq 返回是 Some,获取其中的值,判断获得的 sequence 中的元素的个数是不是三个
若是是三个,则把三个元素分别取出,赋值给 first,second 和 third
3) 其它的规则不变.

object MatchObjectDemo2 {
  def main(args: Array[String]): Unit = {
    val namesString = "Alice,Bob,Thomas" //字符串
    //说明
    namesString match {
      // 当 执行 case Names(first, second, third)
      // 1. 会调用 unapplySeq(str),把 "Alice,Bob,Thomas" 传入给 str
      // 2. 若是 返回的是 Some("Alice","Bob","Thomas"),分别给 (first, second, third)
      // 注意,这里的返回的值unapplySeq的个数须要和 case(first, second, third)要同样
      // 3. 若是返回的 None ,表示匹配失败
      case Names(first, second, third) => {
          println("the string contains three people's names")
        // 打印字符串
        println(s"$first $second $third")
      }
      case _ => println("nothing matched")
    }
  }
}
//object
object Names {
  //当构造器是多个参数时,就会触发这个对象提取器
  def unapplySeq(str: String): Option[Seq[String]] = {
    if (str.contains(",")) Some(str.split(","))
    else None
  }
}

变量声明中的模式

object MatchVarDemo {
  def main(args: Array[String]): Unit = {
    val (x, y, z) = (1, 2, "hello")
    println("x=" + x)
    val (q, r) = BigInt(10) /% 3 //说明 q = BigInt(10) / 3 r = BigInt(10) % 3
    val arr =Array(1, 7, 2, 9)
    val Array(first, second, _*) = arr // 提出 arr 的前两个元素
    println(first, second)
  }
}

for 表达式中的模式

object MatchForDemo {
  def main(args: Array[String]): Unit = {
    val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
    for ((k, v) <- map) {
      println(k + " -> " + v) // 出来三个 key-value ("A"->1), ("B"->0), ("C"->3)
    }
    //说明 : 只遍历出 value =0 的 key-value ,其它的过滤掉
    println("--------------(k, 0) <- map-------------------")
    for ((k, 0) <- map) {
      println(k + " --> " + 0)
    }
    //说明, 这个就是上面代码的另外写法, 只是下面的用法灵活和强大
    println("--------------(k, v) <- map if v == 0-------------------")
    for ((k, v) <- map if v >= 1) {
      println(k + " ---> " + v)
    }
  }
}

样例(模板)类

基本介绍
1) 样例类仍然是类
2) 样例类用 case 关键字进行声明。
3) 样例类是为 模式匹配而优化的类
4) 构造器中的每个参数都成为 val——除非它被显式地声明为 var(不建议这样作)
5) 在样例类对应的伴生对象中提供 apply 方法让你不用 new 关键字就能构造出相应的对象
6) 提供 unapply 方法让模式匹配能够工做
7) 将自动生成 toString 、equals 、hashCode 和 和 copy 方法(有点相似模板类,直接给生成,供程序员使用)
8) 除上述外,样例类和其余类彻底同样。你能够添加方法和字段,扩展它们

object CaseClassDemo02 {
  def main(args: Array[String]): Unit = {
    //该案例的做用就是体验使用样例类方式进行对象匹配简洁性
    for (amt <- Array(Dollar2(1000.0), Currency2(1000.0, "RMB"), NoAmount2)) {
      val result = amt match {
        //说明
        case Dollar2(v) => "$" + v // $1000.0
        //说明
        case Currency2(v, u) => v + " " + u // 1000.0 RMB
        case NoAmount2 => "" // ""
      }
      println(amt + ": " + result)
    }
  }
}
abstract class Amount2
case class Dollar2(value: Double) extends Amount2 //样例类
case class Currency2(value: Double, unit: String) extends Amount2 //样例类
case object NoAmount2 extends Amount2 //样例类

语句的中置(缀)表达式
基本介绍
什么是中置表达式?1 + 2,这就是一个中置表达式。若是 unapply 方法产出一个元组,你能够在
case 语句中使用中置表示法。好比能够匹配一个 List 序列 

object MidCase {
  def main(args: Array[String]): Unit = {
    List(1, 3, 5, 9) match { //修改并测试
      //1.两个元素间::叫中置表达式,至少 first,second 两个匹配才行.
      //2.first 匹配第一个 second 匹配第二个, rest 匹配剩余部分(5,9)
      case first :: second :: rest => println(first + " " + second + " "+ rest.length + " " + rest) //
      case _ => println("匹配不到...")
    }
  }
}

匹配嵌套结构案例

最佳实践案例-商品捆绑打折出售
如今有一些商品,请使用 Scala 设计相关的样例类,完成商品捆绑打折出售。要求
1) 商品捆绑能够是单个商品,也能够是多个商品。
2) 打折时按照折扣 x 元进行设计.
3) 可以统计出全部捆绑商品打折后的最终价格

object Test{
  def main(args: Array[String]): Unit = {
    //这里给出了一个具体的打折的案例
    // 120.
    val sale = Bundle("书籍", 10, Book("漫画", 40), Bundle("文学做品", 20, Book(" 《阳关》", 80), Book("《围城》", 30)))

    //知识点 1 - 使用 case 语句,获得 "漫画"
    val res = sale match {
      //若是咱们进行对象匹配时,不想接受某些值,则使用_ 忽略便可,_* 表示全部
      case Bundle(_, _, Book(desc, _), _*) => desc
    }
    println("res=" + res) //
    //知识点 2-经过@表示法将嵌套的值绑定到变量。_*绑定剩余 Item 到 rest
    val res2 = sale match {
      //若是咱们进行对象匹配时,不想接受某些值,则使用_ 忽略便可,_* 表示全部
      case Bundle(_, _, art @ Book(_, _), rest @ _*) => (art, rest)
    }
    println("res2=" + res2)
    //知识点 3-不使用_*绑定剩余 Item 到 rest
    val res3 = sale match {
      //若是咱们进行对象匹配时,不想接受某些值,则使用_ 忽略便可,_* 表示全部
      case Bundle(_, _, art3 @ Book(_, _), rest3) => (art3, rest3)
    }
    println("res3=" + res3)

    //结果
    println("price=" + price(sale)) // 120

  }

  def price(it:Item): Double = {
    it match {
      case Book(_,p) => p
      case Bundle(_,disc,its @ _*) => its.map(price).sum - disc
    }
  }
}

密封类

基本介绍

1)若是想让case类的全部子类都必须在申明该类的相同的源文件中定义,能够将样例类的通用超类声明为sealed,这个超类称之为密封类。

2)密封就是不能在其余文件中定义子类。