面向对象技术把数据封装到了对象内部,而样本类的作用恰好相反,在必要的时候可以把对象变成一组值。模式匹配是让我们像对字符串使用正则表达式一样从对象中快速获取自己想要的值。

CaseClass

Case Class ,样本类,就是在类定义的前边加了case关键字的类。我们来看一组关于表达式的定义(这个例子来自于《Programming in Scala》)

abstract class Expr
case class Var(name:String) extends Expr
case class Number(num:Double) extends Expr
case class UnOp(op:String,agr:Expr) extends Expr
case class BinOp(op:String,left:Expr,right:Expr) extends Expr

在类的定义前边加上了case,类会自动给我们加上以下功能:

  1. 构造函数里的参数自动加上 val 成为了类的public 字段。
  2. 会自动给类生成伴生对象,并实现了apply 和unapply方法。(unapply方法用来对类进行分解成字段)
  3. 编译器会帮我们加上toString,hashCode 和 equals等的实现。从而可以进行有意义的相等比较。

我们可以来试一下

val exp1 = BinOp("+",Number(3),Var("a"))
println(exp1)  //BinOp(+,Number(3.0),Var(a))
val exp2 = BinOp("+",Number(3),Var("a"))
println(exp1==exp2)  //true

我们想要写一个方法基于以下规则来化简表达式:

  1. 一个表达式加上0,还是本身。
  2. 一个表达式前边有两个负号,还是本身。
val exp1 = BinOp("+",Number(0),Var("a"))
val exp2 = UnOp("-",UnOp("-",Var("a")))
def simplify(expr:Expr)=expr match{
  case BinOp("+",Number(0),e)=>e
  case BinOp("+",e,Number(0))=>e
  case UnOp("-",UnOp("-",e))=>e
  case _=>expr
}
println(simplify(exp1)) //Var(a)
println(simplify(exp2)) //Var(a)

通过上边的例子你可以看到模式匹配的强大威力了吧。语句表达非常精简。
下来我们来完整看一下模式匹配都支持哪些类型:

模式匹配的类型

常量模式

def convert(a:Any) {
  a match {
    case 1 => 1
    case "One" => 1
    case true=> 1
    case _=> 1
  }
}

变量模式

for(i <- 1 to 8){
 i match {
   case a if a > 5 => println(s"$a is bigger that 5")
   case b => println(s"$b is not bigger that 5")
 }
}

上边的例子我们要注意以下点:

  1. 我们给每一种case要匹配的值起了个名字a,b,然后在该case后边的语句里就可以使用了。
  2. 我们发现在case匹配语句里可以有条件判断,比如 case a if a>5,这叫做守卫模式。这个(a>5)是你进入这个模式匹配成功语句的守卫。
  3. 模式匹配的case语句是按顺序执行,如果有一条匹配成功,则后边的都忽略。

变量模式里的变量名必须是小写字母开头的。如果以大写字母开头,Scala会寻找这个名字的字段值来做常量匹配。

通配模式

其实我们之前已经见过通配模式了,就是

case _

这个下划线可以匹配一切,相当于Java里switch语句的default子句。

构造器模式

构造器模式就是在本节最开始看到的case class

序列模式

我们可以通过下边代码来匹配一个序列

val list = List(1,2,3,4)
list match{
  case List(1,2,3,4)=>println("found you")
  case _=>"missed you"
}

用以下几种模式都是可以成功匹配 List(1,2,3,4) 的:

  1. List(1,2,3,4)
  2. List(1,_,_,4)
  3. List(1,_*)

*可以匹配剩余其他,必须放在最后,比如这样的样式就是不行的List(1,*,4)

元组模式

val tuple=(1,"a")
tuple match{
  case (a,b)=>println(a,b)
  case _=>"missed you"
}

元组模式我们之前已经见过了。它也可以这样使用:

val tuple=(1,"a")
val (a,b)=tuple

类型模式

类型模式是为了替换Java里的 instanceof 和 类型强转

def getType(obj:Any):String={
  obj match{
    case _:Int=>"Integer"
    case _:String=>"String"
    case _=>"Unknown"
  }
}

我们可以在匹配值后边通过冒号和类型来进行类型匹配。

for语句里的模式匹配

case class Student(age:Int)
val students = List(12,Student(30),Student(20))
for(Student(age)<-students){
  println(age)
}

students这个list里不光有Student的对象,但是我们在for语句里用构造器模式来解析Student里的age,最终结果是 Scala 跳过了“12”,不会出任何异常,并且成功的打印出了

30
20

模式匹配让我们的代码在面临分支情况,以及类型匹配的时候更加优雅易读,并且让我们能快速的获取我们想要的值,从而让我们在编码时更关注于逻辑,而不是代码流程。这也是Scala贯穿始终的哲学。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: