摘要:case Nothing => println("nothing") //样例对象没有()。单纯的通配符模式通常在模式匹配的最后一行出现,case _ => 它可以匹配任何对象,用于处理所有其它匹配不成功的情况。

好程序员大数据教程分享Scala系列之模式匹配和样例类

1.样例类

在Scala中样例类是一中特殊的类,样例类是不可变的,

可以通过值进行比较,可用于模式匹配。

定义一个样例类:

构造器中每一个参数都是val,除非显示地声明为var

伴生对象提供apply ,让你不使用new关键字就能构造出相应的对象

case class Point(x: Int, y: Int)

创建样例类对象:

val point = Point(1, 2)

val anotherPoint = Point(1, 2)

val yetAnotherPoint = Point(2, 2)

//访问对象值

point.x

point.x =1 //不可以

通过值对样例类对象进行比较:

if (point == anotherPoint) {

println(point + " and " + anotherPoint + " are the same.")

} else {

println(point + " and " + anotherPoint + " are different.")

}

// Point(1,2) 和 Point(1,2)一样的.

if (point == yetAnotherPoint) {

println(point + " and " + yetAnotherPoint + " are the same.")

} else {

println(point + " and " + yetAnotherPoint + " are different.")

}

// Point(1,2)和Point(2,2)是不同的.

样例类的拷贝

You can create a (shallow) copy of an instance of a case class simply by using the copy method. You can optionally change the constructor arguments.

case class Message(sender: String, recipient: String, body: String)

val message4 = Message("[email protected]", "[email protected]", "Me zo o komz gant ma amezeg")

val message5 = message4.copy(sender = message4.recipient, recipient = "[email protected]")

message5.sender // [email protected]

message5.recipient // [email protected]

message5.body // "Me zo o komz gant ma amezeg"

在模式匹配中使用样例类:

abstract class Amount

// 继承了普通类的两个样例类

case class Dollar(value: Double) extends Amount

case class Currency(value: Double, unit: String) extends Amount

case object Nothing extends Amount

object CaseClassDemo {

def main(args: Array[String]): Unit = {

val amt = new Dollar(10);

patternMatch(amt)

}

def patternMatch(amt: Amount) {

amt match {

case Dollar(v) => println("$" + v)

case Currency(_, u) => println("Oh noes, I got " + u)

case Nothing => println("nothing") //样例对象没有()

}

}

}

声明样例类 ,以下几件事会自动发生:

提供unapply方法,让模式匹配可以工作

生成toString equals hashCode copy 方法,除非显示给出这些方法的定义。

2.模式匹配

1.更好的switch

Scala中类似Java的switch代码:

注意:

Scala的模式匹配只会匹配到一个分支,不需要使用break语句,因为它不会掉入到下一个分支。 match是表达式,与if表达式一样,是有值的:

object PatternDemo {

def main(args: Array[String]): Unit = {

var sign = 0

val ch: Char = 'p'

val valchar = 'p'

var digit = 0

//match 是表达式

ch match {

case '+' => sign = 1

case '-' => sign = -1

//使用|分割多个选项

case '*' | 'x' => sign = 2

//可以使用变量

//如果case关键字后面跟着一个变量名,那么匹配的表达式会被赋值给那个变量。

case valchar => sign = 3

//case _ 类似Java中的default

// 如果没有模式能匹配,会抛出MacthError

//可以给模式添加守卫

case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10)

}

println("sign = "+ sign)

}

}

1常量模式(constant patterns) 包含常量变量和常量字面量

scala> val site = "alibaba.com"

scala> site match { case "alibaba.com" => println("ok") }

scala> val ALIBABA="alibaba.com"

//注意这里常量必须以大写字母开头

scala> def foo(s:String) { s match { case ALIBABA => println("ok") } }

常量模式和普通的 if 比较两个对象是否相等(equals) 没有区别,并没有感觉到什么威力

2 变量模式(variable patterns)

确切的说单纯的变量模式没有匹配判断的过程,只是把传入的对象给起了一个新的变量名。

scala> site match { case whateverName => println(whateverName) }

上面把要匹配的 site对象用 whateverName 变量名代替,所以它总会匹配成功。不过这里有个约定,对于变量,要求必须是以小写字母开头,否则会把它对待成一个常量变量,比如上面的whateverName 如果写成WhateverName就会去找这个WhateverName的变量,如果找到则比较相等性,找不到则出错。

变量模式通常不会单独使用,而是在多种模式组合时使用,比如

List(1,2) match{ case List(x,2) => println(x) }

里面的x就是对匹配到的第一个元素用变量x标记。

3 通配符模式(wildcard patterns)

通配符用下划线表示:"_" ,可以理解成一个特殊的变量或占位符。 单纯的通配符模式通常在模式匹配的最后一行出现,case _ => 它可以匹配任何对象,用于处理所有其它匹配不成功的情况。 通配符模式也常和其他模式组合使用:

scala> List(1,2,3) match{ case List(_,_,3) => println("ok") }

上面的 List(_,_,3) 里用了2个通配符表示第一个和第二个元素,这2个元素可以是任意类型 通配符通常用于代表所不关心的部分,它不像变量模式可以后续的逻辑中使用这个变量。

4.样例类匹配

//定义样例类

abstract class Notification

case class Email(sender: String, title: String, body: String) extends Notification

case class SMS(caller: String, message: String) extends Notification

case class VoiceRecording(contactName: String, link: String) extends Notification

//基于样例类的模式匹配

def showNotification(notification: Notification): String = {

notification match {

case Email(email, title, _) =>

s"You got an email from $email with title: $title"

case SMS(number, message) =>

s"You got an SMS from $number! Message: $message"

case VoiceRecording(name, link) =>

s"you received a Voice Recording from $name! Click the link to hear it: $link"

}

}

val someSms = SMS("12345", "Are you there?")

val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")

println(showNotification(someSms)) //结果:You got an SMS from 12345! Message: Are you there?

println(showNotification(someVoiceRecording)) //结果:you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123

2.带守卫的模式

增加布尔表达式或者条件表达式使得匹配更具体。

def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {

notification match {

//仅匹配email在importantPeople列表里的内容

case Email(email, _, _) if importantPeopleInfo.contains(email) =>

"You got an email from special someone!"

case SMS(number, _) if importantPeopleInfo.contains(number) =>

"You got an SMS from special someone!"

case other =>

showNotification(other) // nothing special, delegate to our original showNotification function

}

}

val importantPeopleInfo = Seq("867-5309", "[email protected]")

val someSms = SMS("867-5309", "Are you there?")

val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")

val importantEmail = Email("[email protected]", "Drinks tonight?", "I'm free after 5!")

val importantSms = SMS("867-5309", "I'm here! Where are you?")

println(showImportantNotification(someSms, importantPeopleInfo))

println(showImportantNotification(someVoiceRecording, importantPeopleInfo))

println(showImportantNotification(importantEmail, importantPeopleInfo))

println(showImportantNotification(importantSms, importantPeopleInfo))

5.类型匹配

可以对表达式类型进行匹配:

val arr = Array("hs", 1, 2.0, 'a')

val obj = arr(Random.nextInt(4))

println(obj)

obj match {

case x: Int => println(x)

case s: String => println(s.toUpperCase)

case _: Double => println(Int.MaxValue)

case _ => 0

}

注意: 当你在匹配类型的时候,必须给出一个变量名,否则你将会拿对象本身来进行匹配:

obj match {

case _: BigInt => Int.MaxValue // 匹配任何类型为BigInt的对象

case BigInt => -1 // 匹配类型为Class的BigInt对象

}

匹配发生在运行期,Java虚拟机中泛型的类型信息是被擦掉的。因此,你不能用类型来匹配特定的Map类型。

case m: Map[String, Int] => ... // error

// 可以匹配一个通用的映射

case m: Map[_, _] => ... // OK

// 但是数组作为特殊情况,它的类型信息是完好的,可以匹配到Array[Int]

case m: Array[Int] => ... // OK

3.匹配数组、列表、元组

数组匹配

val arr1 = Array(1,1)

val res = arr1 match {

case Array(0) => "0"

//匹配包含0的数组

case Array(x, y) => s"$x $y"

// 匹配任何带有两个元素的数组,并将元素绑定到x和y

case Array(0, _*) => "0..."

//匹配任何以0开始的数组

case _ => "something else"

}

列表匹配

val lst = List(1,2)

val res2 = list match {

case 0 :: Nil => "0"

case x :: y :: Nil => x + " " + y

case 0 :: tail => "0 ..."

case _ => "something else"

}

元组匹配

var pair = (1,2)

val res3 = pair match {

case (0, _) => "0 ..."

case (y, 0) => s"$y 0"

case _ => "neither is 0"

}

4.Sealed 类(密封类,备选)

Scala中,Traits 和classes可以被关键字sealed修饰, 修饰,被该关键字修饰后,它所有的子类都必须在同一文件中被定义。

这样做的好处是:当你用样例类来做模式匹配时,你可以让编译器确保你已经列出了所有可能的选择,编译器可以检查模式语句的完整性。

sealed abstract class Furniture

case class Couch() extends Furniture

case class Chair() extends Furniture

//此时无需定义能匹配所有的类型了

def findPlaceToSit(piece: Furniture): String = piece match {

case a: Couch => "Lie on the couch"

case b: Chair => "Sit on the chair"

}

相关文章