scala 模式匹配 match


一、match 基本介绍

scala 中的模式匹配类似于 Java 中的 switch 语法, 但是更加强大;

模式匹配语法中, 采用 match 关键字声明, 每个分支采用 case 关键字进行声明, 当需要匹配时,会从第一个 case 分支开始, 如果匹配成功, 那么执行对应的逻辑代码, 如果匹配不成功, 继续执行下一个分支进行判断; 如果所有 case 都不匹配, 那么会执行 case _ 分支, 类似于 Java 中 default 语句;

def main(args: Array[String]): Unit = {
        val a: Int = 1
        val b: Int = 2
        val op: String = StdIn.readLine("请输入一个运算符: ")
        val Plus = "+"
        op match {
            case "+" => println(s"$a + $b = ${a + b}")
            case "-" => println(s"$a - $b = ${a - b}")
            case "*" => println(s"$a * $b = ${a * b}")
            case "/" => println(s"$a / $b = ${a / b}")
            case _ => println("运算符输入有误...")
        }
    }

二、match 的细节和注意事项

  1. 如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中的 default 语句
  2. 如果所有 case 都不匹配,又没有写 case _ 分支,那么会抛出 MatchError
  3. 每个 case 中,不用 break,自动终端 case
  4. 可以在 match 中使用其他类型,而不仅仅是字符
  5. => 等价于 Java switch 的 :
  6. => 后面的代码块到下一个 case,是作为一整体执行,可以使用 {} 括起来, 也可以不括
    在这里插入图片描述

三、模式匹配之条件守卫

概念: 如果想要表达匹配某个范围的数据,就需要在 模式匹配中增加条件守卫

def main(args: Array[String]): Unit = {
    val list = List(1, 2, 'a', "abc", "+", "-", true)
    for (c <- list) {
        c match {
            //如果 c 为 Int 类型的
            case a: Int => println(a + 1)
            //如果 c 为 "+"
            case "+" => println(1)
            //如果 c 为 "-"
            case "-" => println(1)
            //如果 c 为变量
            case a => println("不满足条件的变量: " + a)
        }
    }
    println("---------------------")

    // "+"、"-" 都输出 1, 简化一下
    for (c <- list) {
        c match {
            case a: Int => println(a + 1)
            case op: String if op == "+" || op == "-" => println(1)
            case a => println("不满足条件的变量: " + a)
        }
    }
}

四、模式匹配之变量匹配、常量匹配

变量匹配: 在 case 关键字后跟变量名,那么 match 前表达式的值会赋值给那个变量
常量匹配: 常量的首字母必须大写

def main(args: Array[String]): Unit = {
    val a: Int = 1
    val b: Int = 2
    val op: String = StdIn.readLine("请输入一个运算符: ")
    val Plus = "+"
    op match {
        //常量匹配: 常量的首字母必须大写
        case Plus => println(s"plus: $a + $b = ${a + b}")
        //变量匹配
        case "+" => println(s"$a + $b = ${a + b}")
        case "-" => println(s"$a - $b = ${a - b}")
        case "*" => println(s"$a * $b = ${a * b}")
        case "/" => println(s"$a / $b = ${a / b}")
        case aa => println(s"aa = ${aa}")
        case _ => println("运算符输入有误...")
    }
}

五、模式匹配之类型匹配

概念:可以匹配对象的任意类型,这样做避免了使用 isInstanceOfasInstanceOf 方法

/**
 * scala 模式匹配之: 类型匹配
 *     可以匹配 对象的任意类型, 这样做避免了使用 isInstanceOf 和 asInstanceOf 方法
 */
def main(args: Array[String]): Unit = {
    val a:Int = StdIn.readInt()
    //说明 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")

    println(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] => "对象是一个字符串数组" //对象是一个字符串数组
        case e: Array[Int] => "对象是一个数字数组"
        case f: BigInt => Int.MaxValue
        case _ => "啥也不是"
    }
    println(s"result: ${result.toString}")
}

从1-8 的运行结果:

1
result: 啥也不是
result: 2147483647
result: 对象是一个字符串-数字的 Map集合
result: 对象是一个字符串-数字的 Map集合
result: 对象是一个数字数组
result: 啥也不是
result: 对象是一个字符串数组

从上面的结果我们可以看见,Map 的 K,V 类型不一样,返回的结果却是一样的,原因是 泛型擦除,Java 中,泛型的声明只存在于 .java 文件中,在 编译后的.class 文件中被擦除掉了,而 scala 最终会翻译成为 .class 文件,即此处 Map 的泛型被擦除了,所以不管声明 Map 的 K,V 为何种类型,都会被擦除掉

既然会被 泛型擦除,那么我们的写法就可以简写成:

//case b: Map[String, Int] => "对象是一个字符串-数字的 Map集合"
//case c: Map[Int, String] => "对象是一个数字-字符串的 Map集合"

//改写成
case g: Map[_, _] => "泛型擦除 -- 对象是一个数字-字符串的 Map集合"

scala 中的 List 也会有泛型,同样的这里也会被泛型擦除,如果有,也可以简写成 : case q: List[_] => 对象时 List 类型的集合

后面我们还会说到 List 匹配Map 匹配

类型匹配注意事项:

  1. Map[String, Int] 和 Map[int, String]是两种不同的类型, 其他类推
  2. 在进行类型匹配时,编译器会预先检测是否有可能的匹配,如果没有则报错
    在这里插入图片描述
  3. 如果 case _ 出现在 match 中间,则表示隐藏变量名,即不适用,而不是表示默认匹配
    在这里插入图片描述

六、模式匹配之数组匹配

def main(args: Array[String]): Unit = {
   //        foo(Array(1, 3, 2.3))
   //       foo(Map(1 -> 97.2))
   //        foo(1.1 :: Nil)


    val arr = Array(10, 20, 30, 40)
    arr match {
        //全部匹配
        //case Array(10, 20, 30, 40) => println("Array(10, 20, 30, 40)...")
        //占位符匹配
        //case Array(10, _, _, _) => println("Array(10, _, _, _)...")
        //获取匹配对象中的参数
        //case Array(10, a, _, b) => println(s"Array(10, $a, _, $b)")
        //通配符匹配
        //case Array(10, _*) => println(s"Array(10, _*)")
        //通配符匹配, 将部分元素转为 集合
        case Array(10, a@_*) => println(s"Array(10, a@_*) ${a.mkString(",")}")
        case _ =>
    }
}

def foo(obj: Any): Unit = {
    obj match {
        //匹配数组类型 new int[]
        case a: Array[_] => println(s"Array[Int] ${a.mkString(", ")}")
        case a: Map[_, _] => println(s"Map[_, _]")
        case a: List[_] => println(s"List[_]")
        case _: Int =>
        case _ =>
    }
}

七、模式匹配之集合匹配

def main(args: Array[String]): Unit = {
    val list = List(1, 2, 4, 10)
    list match {
        //case List(_, a, _, _) => println(a)
        //case List(a, b@_*) => println(s"$a, $b")
        case a :: b :: c :: d :: Nil => println(s"$a, $b, $c, $d")
    }
}

八、模式匹配之元组匹配

def main(args: Array[String]): Unit = {
    val t: (Int, Int, Int) = (1, 2, 3)
    t match {
        //全部匹配
        //case (a, b, c) => println(a, b, c)
        //部分匹配
        //case (1, _, c) => println(c)
        //报错
        case (a: Int, b: Int, c: Double) => println(a, b, c)
        case _ => println("类型不匹配")
    }
}

九、模式匹配之变量声明匹配

match 中, 每一个 case 都可以单独提取出来,意思是一样的

//变量声明中的模式匹配
val array = Array(10, 20, 30)
//提取 array 的后两个元素
val Array(_, a, b) = array
println(s"${a}, ${b}")

//提取 array 的前两个元素, 并将后一个元素取为 集合
val Array(d, e, f@_*) = array
println(d, e, f)

//取模求余
val (k, v): (BigInt, BigInt) = BigInt(10) /% 3
println(k, v)

十、模式匹配之对象匹配

object MatchDemo8 {
    /**
      * 对象匹配: 什么才算是匹配呢? 匹配规则如下:
      *     1. case 中对象的 unapply 方法(对象提取器) 返回 Some 集合则为匹配成功
      *     2. 返回 None 集合则为匹配失败
      */
    def main(args: Array[String]): Unit = {
        //先进入 Square 的 apply 方法得到 5 * 5
        val number: Double = Square(5.0) //35.0
        number match {
            //case Square(n) 的运行机制
            //1. 当匹配到 case Square(n)
            //2. 调用 Square 的 unapply(z: Double), z 的值就是 number
            //3. 如果对象提取器 unapply(z: Double) 返回的是 Some(6), 则表示匹配成功
                //同时将 6 赋给 Square(n) 的 n
            //4. 如果对象提取器 unapply(z: Double) 返回的是 None, 则表示匹配失败
            case Square(n) => println(s"匹配成功 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(s"unapply 被调用 z = ${z}")
        //None
        Some(z)
    }

    def apply(z: Double): Double = z * z
}

以上是对象匹配的一种匹配规则:

  1. case 中对象的 unapply 方法(对象提取器) 返回 Some 集合则为匹配成功
  2. 返回 None 集合则为匹配失败

对象匹配的第二种匹配规则:

  1. 2.11.1 之后, 返回值 Option 不是必须的了, 也可以返回其他的类型的对象
  2. 必须要有两个方法
    1. isEmpty:表示是否匹配成功,true 成功,false 成功
    2. get:如果成功,则从这个方法获取具体的匹配到的值
object MatchDemo9 {
    def main(args: Array[String]): Unit = {
        val r: Double = 9
        r match {
            // 将 r 传给 My 的 unapply 的形参, 如果 isEmpty 为 true,
            // 则将 get 返回的值 给到 该形参 a, 最后打印最终结果 a
            case My(a) => println(a)
            case _ =>
        }
    }
}

object  My {
    def unapply(arg: Double): My = new My(arg)
}
class My(r: Double) {
    //表示是否匹配成功, true 成功, false 成功
    def isEmpty: Boolean = r < 0
    //如果成功, 则从这个方法获取具体的匹配到的值
    //如果失败, 则不做任何操作
    def get: Double = math.sqrt(r)
}

十一、模式匹配之样例(模板)类

样例类说明:

  1. 样例类仍然是类
  2. 样例类用 case 关键字进行声明
  3. 样例类是为 模式匹配而优化 的类
  4. 构造器中的每一个参数都成为 val – 除非它被显示地声明为 var(不建议这样做)
  5. 在样例类对应的伴生对象中提供 apply 方法让你不用 new 关键字就能构造出相应的对象
  6. 提供 unapply 方法让模式匹配可以工作
  7. 将自动生成 toString、equals、hashCode 和 copy 方法(有点类似模板类,直接给生成,供程序员使用)
  8. 除上述外,样例类和其他类完全一样。你可以添加方法和子弹,扩展他们
object MatchDemo10 {
    def main(args: Array[String]): Unit = {
        val user = new MatchUser("kino", 18)

        user match {
            case MatchUser(name, age) => println(name, age)
        }

        val MatchUser(name, age) = user
        println(name, age)
    }
}

//object MatchUser {
//    def unapply(arg: MatchUser): Option[(String, Int)] = Some((arg.name, arg.age))
//    def apply(name: String, age: Int) = new MatchUser(name, age)
//}

case class MatchUser(val name: String, val age: Int)
class MatchUser1(val name: String, val age: Int)

十二、case 语句的中置(缀)表达式

什么是中置表达式?

1 + 2 就是一个中置表达式

如果 unapply 方法产出的一个元组, 你可以在 case 语句中使用中置表达法。比如可以匹配一个 List 序列

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 :: fourth => println(s"$first, $second, $rest, ${fourth.length}, ${fourth}")
        case a :: b :: c :: d :: f => println(s"$a, $b, $c, $d, $f, f的长度: ${f.length}")
        case _ => println("匹配不到")
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值