Scala 模式匹配和样例类

    模式匹配是Scala中非常有特色,非常强大的一种功能。模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。

    但是Scala的模式匹配的功能比Java的swich case语法的功能要强大地多,Java的swich case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配、甚至对有值或没值(Option)进行匹配。

    而且对于Spark来说,Scala的模式匹配功能也是极其重要的,在spark源码中大量地使用了模式匹配功能。因此为了更好地编写Scala程序,并且更加通畅地看懂Spark的源码,学好模式匹配都是非常重要的。

 

1.match

    Scala 中的模式匹配类似于 Java 中的 switch 语法,但比Java 的 switch 更加强大。

/**
 * 1) 如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句
 * 2) 如果所有 case 都不匹配,又没有写 case _ 分支,那么会抛出 MatchError
 * 3) 每个 case 中,不用 break 语句,自动中断 case
 * 4) 可以在 match 中使用其它类型,而不仅仅是字符
 * 5) => 等价于 java swtich 的 :
 * 6) => 后面的代码块到下一个 case, 是作为一个整体执行,可以使用{} 扩起来,也可以不扩。
 */
object Scala01_MatchDemo01 {
  def main(args: Array[String]): Unit = {
    val oper = '+'
    val n1 = 20
    val n2 = 10
    var res = 0

    oper match {
      case '+' => {
        res = n1 + n2
        println("hello~~")
      }
      case '-' => res = n1 - n2
      case '*' => res = n1 * n2
      case '/' => res = n1 / n2
      case 1 => println("匹配到1")
      case 1.1 => println("匹配1.1")
      case _ => println("oper error")
    }
    println("res=" + res)

  }
}

2.守卫

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

object Scala02_MatchIfDemo01 {
  def main(args: Array[String]): Unit = {
    for (ch <- "+-3!") {
      var sign = 0
      var digit = 0
      ch match {
        case '+' => sign = 1
        case '-' => sign = -1
        case _ if ch.toString.equals("3") => digit = 3
        case _ if (ch > 1110 || ch < 120) => println("ch > 10")
        case _ => sign = 2
      }
      println(ch + " " + sign + " " + digit)
    }
  }
}

3.模式中的变量

    如果在case关键字后跟变量名,那么match前表达式的值会赋给那个变量。

/**
 * 在模式匹配中进行变量赋值
 * Scala的模式匹配语法,有一个特点在于,可以将模式匹配的默认情况,下划线,替换为一个变量名,此时模式匹配语法就会将要匹配的值赋值给这个变量,从而可以在后面的处理语句中使用要匹配的值。
 * 为什么有这种语法??思考一下。因为只要使用用case匹配到的值,是不是我们就知道这个只啦!!在这个case的处理语句中,是不是就直接可以使用写程序时就已知的值。
 * 但是对于下划线_这种情况,所有不满足前面的case的值,都会进入_这种默认情况进行处理,此时如果我们在处理语句中需要拿到具体的值进行处理呢?那就需要使用这种在模式匹配中进行变量赋值的语法。
 */
object Scala03_MatchVarDemo02 {
  def main(args: Array[String]): Unit = {
    def studentScore(name:String,score:String): Unit ={
      score match {
        case "A"=>println("excellent")
        case "B"=>println("good")
        case "C"=>println("soso")
        case _ if name=="leo"=>print(name+",you are good boy,come on!")
        case _score =>println("you need work harder,your score only "+_score)  //变量赋值
      }
    }
    studentScore("le","F")
  }
}

4.类型匹配

    可以匹配对象的任意类型,但是不能直接匹配泛型类型,这样做的意义在于避免了使用isInstanceOf和asInstanceOf方法。

    Scala的模式匹配一个强大之处就在于,可以直接匹配类型,而不是值!!!这点是java的switch case绝对做不到的。

    对类型如何进行匹配?其他语法与匹配值其实是一样的,但是匹配类型的话,就是要用“case 变量: 类型 => 代码”这种语法,而不是匹配值的“case 值 => 代码”这种语法。

object Scala04_MatchTypeDemo01 {
  def main(args: Array[String]): Unit = {
    val a = 8
    //说明 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”)

    //说明:根据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 y: Float => println("xx")
      case _ => "啥也不是"
    }
    println(result)
  }
}

5.匹配数组

object Scala05_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 _ => "什么集合都不是"
          }
          println("result = " + result)
        }
  }
}

6.匹配列表

    与匹配数组相似,同样可以应用于列表。

object Scala06_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"
      }
      println(result)
    }
  }
}

7.匹配元组

    同样可以应用于元组

object Scala07_MatchTupleDemo01 {
  def main(args: Array[String]): Unit = {
    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)
        case _ => "other"
      }
      println(result)
    }
  }
}

8.对象匹配

/**
 * 对象匹配,什么才算是匹配呢?,规则如下:
 * 1) case中对象的unapply方法(对象提取器)返回Some集合则为匹配成功
 * 2) 返回 None 集合则为匹配失败
 */
object Scala08_MatchObjectDemo01 {
  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) ,则表示匹配成功,同时将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
}

9.变量声明中的模式

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

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

  }
}

10.for 表达式中的模式

    for 循环也可以进行模式匹配.

object Scala10_MatchForDemo {
  def main(args: Array[String]): Unit = {
    val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
    for ((k, v) <- map) {
      println(k + " -> " + v)
    }
    //说明 : 只遍历出 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)
    }
  }
}

11.样例类

    1) 样例类仍然是类。

    2) 样例类用 case 关键字进行声明。

    3) 样例类是为模式匹配而优化的类。

    4) 构造器中的每一个参数都成为 val——除非它被显式地声明为 var(不建议这样做)。

    5) 在样例类对应的伴生对象中提供 apply 方法让你不用 new 关键字就能构造出相应的对象。

    6) 提供 unapply 方法让模式匹配可以工作。

    7) 将自动生成 toString、equals、hashCode 和 copy 方法(有点类似模板类,直接给生成,供程序 员使用)。

    8) 除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们。

object Scala11_CaseClassDemo01 {
  def main(args: Array[String]): Unit = {
    println("hello~~")
  }
}
abstract class Amount
case class Dollar(value: Double) extends Amount    //样例类
case class Currency(value: Double, unit: String) extends Amount //样例类
case object NoAmount extends Amount  //样例类

12.case 语句的中置(缀)表达式

    什么是中置表达式?1 + 2,这就是一个中置表达式。如果 unapply 方法产出一个元组,你可以在 case 语句中使用中置表示法。比如可以匹配一个 List 序列。

object Scala12_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("匹配不到...")
    }
  }
}

13.匹配嵌套结构

    操作原理类似于正则表达式。

/**
 * @author mkluo
 * @date 2020/8/3 上午1:42
 * @desc 匹配嵌套结构
 * 现在有一些商品,请使用Scala设计相关的样例类,完成商品捆绑打折出售。要求:
 * 商品捆绑可以是单个商品,也可以是多个商品。
 * 打折时按照折扣x元进行设计.
 * 能够统计出所有捆绑商品打折后的最终价格
 */
object Scala13_SalesDem01 {
  def main(args: Array[String]): Unit = {

    //这里给出了一个具体的打折的案例
    //如果我们进行对象匹配时,不想接受某些值,则使用_ 忽略即可,_* 表示所有
    val sale = Bundle("书籍", 10,  Book("漫画", 40), Bundle("文学作品", 20, Book("《阳关》", 80), Book("《围城》", 30),Book("天龙八部", 100)))

    //知识点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))
  }

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

//设计样例类
abstract sealed class Item // 项
case class Book(description: String, price: Double) extends Item
case class Food(description: String, price: Double) extends Item
case class Bundle(description: String, discount: Double, item: Item*) extends Item

14.密封类

    如果想让case类的所有子类都必须在申明该类的相同的源文件中定义,可以将样例类的通用超类声明为sealed,这个超类称之为密封类。

/**
 * 密封类
 * 1、使用 sealed 修饰的类或者特质,不能在类定义文件之外定义它的子类。
 * 2、可以避免滥用继承。
 * 3、用在模式匹配之中,可以让编译器在编译阶段进行检查,确保当前匹配的完整性。
 */
  sealed abstract class Furniture
  //定义两个样例类
  case class Couch() extends Furniture
  case class Chair() extends Furniture

object Scala14_seale {
    def findPlaceToSit(furniture: Furniture):String =furniture match {
      case a:Couch => "line on couch"
      case b:Chair => "line on Chair"
    }
    val chair = Chair()
    def main(args: Array[String]): Unit = {
      println(findPlaceToSit(chair))
    }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员学习圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值