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))
  }

matches 匹配中文_后端


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)))
  }

matches 匹配中文_List_02


 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("小明"))
  }

matches 匹配中文_开发语言_03


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))
  }

matches 匹配中文_matches 匹配中文_04


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))
  }

matches 匹配中文_后端_05


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))

  }

matches 匹配中文_matches 匹配中文_06


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")

  }

 

matches 匹配中文_matches 匹配中文_07


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))
  }

matches 匹配中文_scala_08

7. Scala中 match和 java中switch的区别

        Scala中match是一个表达式(总能得到一个值)

        Scala的可选分支不会贯穿(full through)到下一个case

        Scala中如果一个模式也没匹配上,会抛出MatchError的异常