匹配常量

1.说明
Scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等

2.案例实操

  1. object TestMatchVal {
  2. def main(args: Array[String]): Unit = {
  3. // 抛异常
  4. println(describe(6))
  5. }
  6. def describe(x: Any) = x match {
  7. case 5 => "Int five"
  8. case "hello" => "String hello"
  9. case true => "Boolean true"
  10. case '+' => "Char +"
  11. }
  12. }

匹配类型

  1. 说明

需要进行类型判断时,可以使用前文所学的 isInstanceOf[T] 和 asInstanceOf[T] ,也可使用模式匹配实现同样的功能

  1. 案例实操 ```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 } }

  1. <a name="z3jA7"></a>
  2. # 匹配数组
  3. **1.说明**<br />scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为0的数组<br />**2.案例实操**
  4. ```scala
  5. object TestMatchArray {
  6. def main(args: Array[String]): Unit = {
  7. //对一个数组集合进行遍历
  8. for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) {
  9. val result = arr match {
  10. //匹配Array(0) 这个数组
  11. case Array(0) => "0"
  12. //匹配有两个元素的数组,然后将将元素值赋给对应的x,y
  13. case Array(x, y) => x + "," + y
  14. //匹配以0开头和数组
  15. case Array(0, _*) => "以0开头的数组"
  16. case _ => "something else"
  17. }
  18. println("result = " + result)
  19. }
  20. }
  21. }

输出

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

  1. 基本语法 ```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"