千家信息网

Scala笔记整理(七):模式匹配和样例类

发表于:2024-09-22 作者:千家信息网编辑
千家信息网最后更新 2024年09月22日,[TOC]可以用到switch语句1、Scala强大的模式匹配机制,可以应用在switch语句、类型检查以及"析构"等场合。def swithOps: Unit ={ var sign = 0
千家信息网最后更新 2024年09月22日Scala笔记整理(七):模式匹配和样例类

[TOC]


可以用到switch语句

1、Scala强大的模式匹配机制,可以应用在switch语句、类型检查以及"析构"等场合。

def swithOps: Unit ={    var sign = 0    val ch: Char = '+'    ch match {        case '+' => sign = 1        case '-' => sign = -1        case _ => sign = 0    }    println("sign===> " + sign)}

上面代码中,case _模式对应于switch语句中的default,能够捕获剩余的情况。如果没有模式能匹配,会抛出MatchError。而且不像常见的switch语句,在一种模式匹配之后,需要使用break来声明分支不会进入下一个分支,在scala中并不需要这种break语句。

2、match是表达式,不是语句,所以是有返回值的,故可将代码简化:

sign = ch match {    case '+' => 1    case '-' => -1    case _ => 0}println("sign=====> "+ sign)

可以查看下面的完整测试案例:

package cn.xpleaf.bigdata.p5.cm/**  * scala中的模式匹配  *     错误: 找不到或无法加载主类 cn.xpleaf.bigdata.p5.`case`.CaseOps  *  *     这里的问题出在将关键字作为类名中的一部分  *     所以今后的书写过程中尽量不要将关键字作为类名或者包名  */object CaseOps {  def main(args: Array[String]): Unit = {    caseOps2  }  /**    * scala中的任何表达式都是有返回值的,模式匹配也不例外,我们可以直接获取对应的返回值进行操作    * 如果不写case _的操作,匹配不上的话,会抛出相关异常:scala.MatchError    */  def caseOps2: Unit = {    val ch = '1'    val sign = ch match {      case '+' => 1      case '-' => 0//      case _ => 2    }    println(sign)  }  /**    * 一般情况下也就是将模式匹配当做java中的switch case来进行使用    */  def caseOps1: Unit = {    val ch = '1'    var sign = -1    ch match {      case '+' => sign = 1      case '-' => sign = 0      case _ => sign = 2    }    println(sign)  }}

输出结果如下:

2

守卫

与if表达式的守卫相同作用,测试案例如下:

object CaseOps {  def main(args: Array[String]): Unit = {    caseOps3  }  /**    * match模式匹配当做守卫进行使用    */  def caseOps3: Unit = {    val ch:Char = 'a'    var sign = -1    ch match {      case '+' => sign = 1      case '-' => sign = -1      case _ if Character.isDigit(ch) => sign = 2      case _ if Character.isAlphabetic(ch) => sign = 3      case _ => sign = -2    }    println("sign = " + sign)  }}

输出结果如下:

sign = 3

模式中的变量

如果在case关键字后跟着一个变量名,那么匹配的表达式会被赋值给那个变量。case _ 是这个特性的一个特殊情况,变量名是_

测试案例如下:

object CaseOps {  def main(args: Array[String]): Unit = {    caseOps4  }  /**    * 将要进行匹配的值,赋值给case后面的变量,我们可以对变量进行各种操作    */  def caseOps4: Unit = {    "Hello, wordl" foreach(c => println(      c match {        case ' ' => "space"        case ch => "Char: " + ch      }    ))  }}

输出结果如下:

Char: HChar: eChar: lChar: lChar: oChar: ,spaceChar: wChar: oChar: rChar: dChar: l

类型模式

相比使用isInstanceOf来判断类型,使用模式匹配更好。

object CaseOps {  def main(args: Array[String]): Unit = {    caseOps5  }  /**    * 使用模式匹配可以代替isInstanceOf和asInstanceOf来进行使用    */  def caseOps5: Unit = {    def typeOps(x: Any): Int = {      val result = x match {        case i: Int => i        case s: String => Integer.parseInt(s)        case z: scala.math.BigInt => Int.MaxValue        case c: Char => c.toInt        case _ => 0      }      result    }    println(typeOps("12345") == 12345)  }}

输出结果如下:

true

匹配数组、列表和元组

测试代码如下:

object CaseOps {  def main(args: Array[String]): Unit = {    caseOps6  }  /**    * 匹配scala数组    */  def caseOps6: Unit = {    val arr = Array(0, 1)    arr match {      //匹配只有一个元素的数组,且元素就是0      case Array(0) => println("0")      // 匹配任何带有两个元素的数组,并将元素绑定到x和y      case Array(x, y) => println(x + " " + y)      // 匹配任何以0开始的数组      case Array(0, _*) => println("0 ...")      case _ => println("something else")    }  }}

输出结果如下:

0 1

样例类

1、样例类是种特殊的类,经过优化以用于模式匹配。

2、Scala中提供了一种特殊的类,用case class进行声明,中文也可以称作样例类。case class其实有点类似于Java中的JavaBean的概念。即只定义field,并且由Scala编译时自动提供getter和setter方法,但是没有method。

3、case class的主构造函数接收的参数通常不需要使用var或val修饰,Scala自动就会使用val修饰(但是如果你自己使用var修饰,那么还是会按照var来)

4、 Scala自动为case class定义了伴生对象,也就是object,并且定义了apply()方法,该方法接收主构造函数中相同的参数,并返回case class对象

测试案例如下:

object CaseOps {  def main(args: Array[String]): Unit = {    caseOps7  }  /**    * 样例类(case class)的模式匹配    */  def caseOps7: Unit = {    abstract class Expr    case class Var(name:String) extends Expr    case class UnOp(operator:String, arg:Expr) extends Expr    case class BinOp(operator:String, left:Expr, right:Expr) extends Expr    def test(expr:Expr) = expr match {      case Var(name) => println(s"Var($name)...")      case UnOp(operator, e) => println(s"$e ... $operator")      case BinOp(operator, left, right) => println(s"$left $operator $right")      case _ => println("default")    }    test(BinOp("+", Var("1"), Var("2")))  }}

输出结果如下:

Var(1) + Var(2)

模拟枚举

可以使用样例类来模拟枚举类型:

当使用样例类来做模式匹配时,如果要让编译器确保已经列出所有可能的选择,可以将样例类的通用超类声明为sealed。

密封类的所有子类都必须在与该密封类相同的文件中定义。

如果某个类是密封的,那么在编译期所有的子类是可知的,因而可以检查模式语句的完整性。

让所有同一组的样例类都扩展某个密封的类或特质是个好的做法。

sealed abstract class TrafficLightColorcase object Red extends TrafficLightColorcase object Yellow extends TrafficLightColorcase object Green extends TrafficLightColordef typeOps(color:TrafficLightColor): Unit ={    color match {        case Red => println("stop")        case Yellow =>println( "hurry up")        case Green => println("go")    }}def verityTypeOps: Unit ={    typeOps(Red)}

Option类型

1、Option类型用来表示可能存在也可能不存在的值。样例子类Some包装了某个值,而样例对象None表示没有值。Option支持泛型。(所以这里跟上面一样,也是样例类的操作)

2、Option的语法必须大量掌握,因为Spark源码中有大量的实例是关于Option语法的。

测试案例如下:

object CaseOps {  def main(args: Array[String]): Unit = {    caseOps8  }  def caseOps8: Unit = {    def optionOps: Unit = {      val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo", "BeiJing" -> "通州")      println(capitals.get("Japan") + show(capitals.get("Japan")))      println(capitals.get("BeiJing") + show(capitals.get("India")))    }    def show(x: Option[String]) = x match {      case Some(s) => s      case None => "?"    }    optionOps  }}

输出结果如下:

Some(Tokyo)TokyoSome(通州)?

3、getOrElse

以下是演示如何使用getOrElse()来访问值或使用默认值,当没有值的例子:

val a:Option[Int] = Some(5)val b:Option[Int] = Noneprintln("a.getOrElse(0): " + a.getOrElse(0) )println("b.getOrElse(10): " + b.getOrElse(10) )

4、使用isEmpty()方法:

println("a.isEmpty: " + a.isEmpty )println("b.isEmpty: " + b.isEmpty )

其测试如下:

scala> val a:Option[Int] = Some(5)a: Option[Int] = Some(5)scala> val b:Option[Int] = Noneb: Option[Int] = Nonescala> a.getOrElse(0)res5: Int = 5scala> ares6: Option[Int] = Some(5)scala> b.getOrElse(10)res9: Int = 10scala> a.isEmptyres7: Boolean = falsescala> b.isEmptyres8: Boolean = true
0