匹配常量
1.说明
Scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等
2.案例实操
object TestMatchVal {
def main(args: Array[String]): Unit = {
// 抛异常
println(describe(6))
}
def describe(x: Any) = x match {
case 5 => "Int five"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
}
}
匹配类型
- 说明
需要进行类型判断时,可以使用前文所学的 isInstanceOf[T] 和 asInstanceOf[T] ,也可使用模式匹配实现同样的功能
案例实操 ```scala object TestMatchClass { def describe(x: Any) = x match { case i: Int => “Int” case s: String => “String hello” case m: List[_] => “List” case c: Array[Int] => “Array[Int]” case someThing => “something else “ + someThing }
def main(args: Array[String]): Unit = { //泛型擦除 println(describe(List(1, 2, 3, 4, 5))) // List
//数组例外,可保留泛型 println(describe(Array(1, 2, 3, 4, 5, 6))) // Array[Int] println(describe(Array(“abc”))) // something else [Ljava.lang.String;@77cd7a0 } }
<a name="z3jA7"></a>
# 匹配数组
**1.说明**<br />scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为0的数组<br />**2.案例实操**
```scala
object TestMatchArray {
def main(args: Array[String]): Unit = {
//对一个数组集合进行遍历
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) {
val result = arr match {
//匹配Array(0) 这个数组
case Array(0) => "0"
//匹配有两个元素的数组,然后将将元素值赋给对应的x,y
case Array(x, y) => x + "," + y
//匹配以0开头和数组
case Array(0, _*) => "以0开头的数组"
case _ => "something else"
}
println("result = " + result)
}
}
}
输出
result = 0
result = 1,0
result = 以0开头的数组
result = something else
result = something else
result = hello,90
匹配列表
1.方式一
object TestMatchList {
def main(args: Array[String]): Unit = {
//list是一个存放List集合的数组
//请思考,如果要匹配 List(88) 这样的只含有一个元素的列表,并原值返回.应该怎么写
for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0), List(88))) {
val result = list match {
case List(0) => "0" //匹配List(0)
case List(x, y) => x + "," + y //匹配有两个元素的List
case List(0, _*) => "0 ..."
case _ => "something else"
}
println(result)
}
}
}
输出
0
1,0
0 ...
something else
something else
方式二:
val list1 = List(1, 2, 5, 7, 24) // first: 1, second: 2, rest: List(5, 7, 24)
val list = List(24) // something else
list1 match {
// 第一个元素, 第二个元素, 剩下的元素
case first :: second :: rest => println(s"first: $first, second: $second, rest: $rest")
case _ => println("something else")
}
匹配元组
object TestMatchTuple {
def main(args: Array[String]): Unit = {
//对一个元组集合进行遍历
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
val result = tuple match {
//是第一个元素是0的元组
case (0, _) => "0 ..."
//匹配后一个元素是0的对偶元组
case (y, 0) => "" + y + "0"
case (a, b) => "" + a + " " + b
//默认
case _ => "something else"
}
println(result)
}
}
}
扩展案例
object TestGeneric {
def main(args: Array[String]): Unit = {
//特殊的模式匹配1 打印元组第一个元素
for (elem <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(elem._1)
}
for ((word,count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(word)
}
for ((word,_) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(word)
}
// 只能匹配到 ("a", 1)
for (("a",count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(count)
}
println("--------------")
//特殊的模式匹配2 给元组元素命名
var (id,name,age): (Int, String, Int) = (100, "zs", 20)
println((id,name,age))
println("--------------")
//特殊的模式匹配3 遍历集合中的元组,给count * 2
var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))
//println(list.map(t => (t._1, t._2 * 2)))
println(
list.map{
case (word,count)=>(word,count*2)
}
)
var list1 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3)))
// List((a,2), (b,4), (c,6))
println(
list1.map{
case (groupkey,(word,count))=>(word,count*2)
}
)
}
}
匹配对象, 提取器unapply
- 基本语法 ```scala class User(val name: String, val age: Int)
object User {
def apply(name: String, age: Int): User = new User(name, age)
def unapply(user: User): Option[(String, Int)] = {
if (user == null)
None
else
Some(user.name, user.age)
}
}
object TestMatchUnapply {
def main(args: Array[String]): Unit = {
val user: User = User(“zhangsan”, 11)
val result = user match {
case User(“zhangsan”, 11) => “yes”
case _ => “no”
}
println(result)
} }
**小结**<br />val user = User(“zhangsan”,11),该语句在执行时,实际调用的是User伴生对象中的 apply 方法,因此不用 new关键字 就能构造出相应的对象。
当将 User("zhangsan", 11) 写在case后时[case User(“zhangsan”, 11) => “yes”],会默认调用 unapply 方法(对象提取器),user作为unapply方法的参数,unapply方法将user对象的name和age属性提取出来,与User(“zhangsan”, 11)中的属性值进行匹配
case中对象的unapply方法(提取器)返回Some,且所有属性均一致,才算匹配成功,属性不一致,或返回None,则匹配失败。
若只提取对象的一个属性,则提取器为 `unapply(obj:Obj):Option[T]`<br />若提取对象的多个属性,则提取器为` unapply(obj:Obj):Option[(T1,T2,T3…)]`<br />若提取对象的可变个属性,则提取器为 `unapplySeq(obj:Obj):Option[Seq[T]]`
<a name="IjBci"></a>
# 布尔提取器
有些时候,进行模式匹配并不是为了提取参数,而是为了检查其是否匹配。 这种情况下,第三种 unapply 方法签名(也是最后一种)就有用了, 这个方法接受 S 类型的对象,返回一个布尔值:
```scala
def unapply(object: S): Boolean
使用的时候,如果这个提取器返回 true ,模式会匹配成功, 否则,Scala 会尝试拿 object 匹配下一个模式。
上一个例子存在一些逻辑代码,用来检查一个免费用户有没有可能被说服去升级他的账户。 其实可以把这个逻辑放在一个单独的提取器中:
object premiumCandidate {
def unapply(user: FreeUser): Boolean = user.upgradeProbability > 0.75
}
你会发现,提取器不一定非要在这个类的伴生对象中定义。 正如其定义一样,这个提取器的使用方法也很简单:
trait User {
def name: String
def score: Int
}
class FreeUser(
val name: String,
val score: Int,
val upgradeProbability: Double
) extends User
object FreeUser {
def unapply(user: FreeUser): Option[(String, Int, Double)] =
Some((user.name, user.score, user.upgradeProbability))
}
val user: User = new FreeUser("Daniel", 2500, 0.8d)
user match {
case freeUser @ premiumCandidate() => initiateSpamProgram(freeUser)
case _ => sendRegularNewsletter(user)
}
使用的时候,只需要把一个空的参数列表传递给提取器,因为它并不真的需要提取数据,自然也没必要绑定变量。
这个例子有一个看起来比较奇怪的地方: 我假设存在一个空想的 initiateSpamProgram 函数,其接受一个 FreeUser 对象作为参数。 模式可以与任何一种 User 类型的实例进行匹配,但 initiateSpamProgram 不行, 只有将实例强制转换为 FreeUser 类型, initiateSpamProgram 才能接受。
因为如此,Scala 的模式匹配也允许将提取器匹配成功的实例绑定到一个变量上, 这个变量有着与提取器所接受的对象相同的类型。这通过 @ 操作符实现。 premiumCandidate 接受 FreeUser 对象,因此,变量 freeUser 的类型也就是 FreeUser 。
样例类
简介
case class主要用于不可变的数据。他们和普通类几乎是一样的。
case class Book(isbn: String)
val frankenstein = Book("978-0486282114")
实例化案例类的时候并不需要new关键字,因为case class有一个默认的apply方法来负责对象的创建。
在case class中,参数是public并且val的,这意味着case class的参数不可变
case class Message(sender: String, recipient: String, body: String)
val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?")
println(message1.sender) // prints guillaume@quebec.ca
message1.sender = "travis@washington.us" // this line does not compile
这里message1.sender不能被重新赋值,因为他是val(不可变)的
apply和unapply
样例类非常特殊,Scala会自动为其创建一个 伴生对象 : 一个包含了 apply 和 unapply 方法的 单例对象 。 apply 方法用来创建样例类的实例,而 unapply 需要被伴生对象实现,以使其成为提取器。
(1)语法
case class Person (name: String, age: Int)
(2)说明
样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如 apply、unapply、toString、equals、hashCode和copy
样例类是为模式匹配而优化的类,因为其默认提供了unapply方法,因此,样例类可以直接使用模式匹配,而无需自己实现unapply方法
构造器中的每一个参数都成为val,除非它被显式地声明为var(不建议这样做)
(3)案例实操
上述匹配对象的案例使用样例类会节省大量代码
case class User(name: String, age: Int)
object TestMatchUnapply {
def main(args: Array[String]): Unit = {
val user: User = User("zhangsan", 11)
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}
比较
case class的比较是按值比较的,而不是按引用:
case class Message(sender: String, recipient: String, body: String)
val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
val messagesAreTheSame = message2 == message3 // true
虽然上面是不同的对象,但是因为他们的值相同,所以最后的比较是true。
拷贝
可以使用copy来做case class的浅拷贝
case class Message(sender: String, recipient: String, body: String)
val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg")
val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr")
message5.sender // travis@washington.us
message5.recipient // claire@bourgogne.fr
message5.body // "Me zo o komz gant ma amezeg"