本篇学习笔记是第二遍看《快学scala》所写,对scala语言有一定的基础
14.1 更好的switch
val ch: Char = '*'
ch match {
case '+' => println("加法")
case '*' => println("乘法")
case _ => println("木有")
}
match是一个表达式,而不是语句,会有返回值
val ch: Char = '*'
val x:Int = ch match {
case '+' => 0
case '*' => 1
case _ => 2
}
println(x)
14.2 守卫
守卫可以是任何布尔条件,在下面的例子中,只有在守卫模式不能匹配的情况下才执行所有模式匹配
val ch:Char = 3
var sign:Int = 0
ch match {
case '+' => println("++++")
case '*' => println("====")
case _ if ch >= 0 && ch <= 9 => sign = ch
case _ => println("---")
}
println(sign)
14.3 模式中的变量
case _
是这种特性的特殊情况,变量名是_
val c: Char = '+'
val ch:Char = 9
var sign:Int = 0
ch match {
case '+' => println("++++")
case '*' => println("====")
case c if c >= 0 && c <= 9 => sign = c
case _ => println("---")
}
println(sign)
- 变量必须以小写字母开头
- 如果有一个小写字母开头的常量,则需要将它包在反引号中。
14.4 类型模式
匹配列表中的每一个元素的类型,将每一个元素映射成=>
后面的string
类型
val ls = List(1,3,4,"one","two",4.5)
val l = ls.map(_ match {
case x:Int => "int x:" + x
case s:String => "string s:" + s
case _ => "other"
})
l.foreach(println)
14.5 匹配组、列表和元组
要匹配数组的内容,可以在模式中使用Array表达式,_*
符号匹配数组中余下的内容
val arr1 = Array(Array(0),Array(3,4),Array(0,4,6),Array("fad","fff"))
arr1.map(_ match {
case Array(0) => println(0)
case Array(x,y) => println(x,y)
case Array(0,_*) => println("...")
case _ => println("something")
})
匹配列表中的元素
val ls = List(List(0),List(3,4),List(0,4,5),List("fa","aaa","tttt"))
ls.map(_ match {
case 0::Nil => println(0)
case x :: y :: Nil => println(x,y)
case 0 :: tail => println(tail.foreach(println))
case x :: tail => println("====")
case _ => println("something")
})
匹配元组
val ls = List((0,4),("fa",0),(3,4))
ls.map(_ match {
case (0,_) => println(0)
case (x,y) => println(x,y)
case _ => println("something")
})
14.6 提取器
前一节中匹配数组、列表和元素的功能是依靠提取器实现的
提取器机制:带有从对象中提取值得unapply
和unapplySeq
方法得对象。unapply
提取固定数量得对象,而unapplySeq
提取得是一个序列
Array.unapplySeq(arr)
产出一个序列的值。
14.8 for表达式中的模式
val ls = List((1,2),(3,4),(4,5))
for ((k,v) <- ls if k % 2 == 1)
println(k,v)
14.9 样例类
可以用模式匹配来匹配样例类,并将属性值绑定到变量
case class Dollar(value:Double)
case class Currency(value:Double,unit:String)
case object Yuan
val ls = List(Dollar(2.3),Currency(4.5,"10"),Yuan)
ls.map(_ match {
case Dollar(v) => println(s"Dollar:$v")
case Currency(_,u) => println(s"got $u")
case Yuan => println("lalala")
case _ => println("----")
})
*样例类实例使用(),样例对象不适用圆括号
==声明样例类的时候有如下几件事自动发生:==
- 构造器中的每一个参数都成为val,除非被显式地声明为var
- 在伴生对象中提供apply方法,可以不用new关键字就能构造出相应的对象,比如Dollar(2.3)
- 提供unapply方法让模式匹配可以工作
- 将生成toString
equals
copy
方法,除非显式给出定义
14.10 copy方法和带名参数
copy方法创建一个与现有对象值相同的新对象,可以用带名参数修改某些属性
val amt = Currency(34.2,"RUR")
val price = amt.copy()
val price1 = amt.copy(value = 13)
val price2 = amt.copy(unit = "CHF")
14.11 case语句中的中置表示法
如果unapply方法产出一个对偶,可以在case语句中使用中置表示法,19章将会看到将解析结果组合在一起的`~
样例类:
result match {case p ~ q => ...} //等同于case ~(p,q)
*如果操作符以冒号结尾,则他是从右向左结合的
中置表示法可以用于任何返回对偶的unapply方法:
case object +: {
def unapply[T](input: List[T]):Option[(T,List[T])] =
if(input.isEmpty) None else Some((input.head,input.tail))
}
1+:7+:2+:Nil match {
case first +: second +: rest => println(first + second + rest.length)
}
14.12 匹配嵌套结构
三个样例类继承Item抽象类,Bundle和Boolean类嵌套Article和Bundle样例类,
可以用@
表示法将嵌套的值绑定到变量
abstract class Item
case class Article(desc:String,price:Double) extends Item//继承同一个抽象类
case class Bundle(desc:String,discount:Double,item:Item) extends Item
val peak = Bundle("Father's Day is special",20.0,Article("scala is nice",45.5))
case class Boolean(desc:String,dis:Double,item:Item*)//Item抽象类列表
val amt = Boolean("great",23.3,Bundle("nice",45.4,Article("wonderful",45.4)),Article("scala is nice",45.5))
val ls = List(peak,amt)
ls.map(_ match {
case Bundle(_,_,Article(desc,price)) => println(s"bundle ${desc}:${price}")
case Boolean(_,_,art @ Bundle(_,_,_),rest @ _*) => println(s"boolean ${art.desc}:${art.discount},${rest.head}")
case Boolean(_,_,art @ Bundle(_,_,_),rest) => println(s"boolean ${art.desc}")//匹配继承Item抽象类的元素只有两个
case _ => println("None")
})
14.14 密封类
当用样例类来做模式匹配的时候,想让编译器帮助确保已经列出了所有可能的选择,要达到这个目的,需要将样例类的通用超类声明为sealed
- 密封类的所有子类都必须在与该密封类相同的文件中定义。
- 如果某个类是密封的,那么在编译期所有子类就是可知的,因此==编译器可以检查模式语句的完整性==,让同一组样例类都扩展自某个密封的类或者特质是一个好的做法
sealed abstract class Amount
case class Red(bottom:Double) extends Amount
case object Green extends Amount
case object Gray extends Amount
val color:Amount = Red(3.4)//需要显式声明color的类型,不然就会变成Red
color match {
case Red(_) => println("red")
case Green =>println("green")
case Gray => println("fad")
}
14.16 Option类型
标准类库的Option类型用样例类来表示那种可能存在,也可能不存在的值。这比空字符串更加清晰,比null做法更加安全。
* map类的get方法返回一个Option。如果对于给定的键没有对应的值,则get返回None,如果有值,该值包在Some中返回
* 请以不要直接使用get方法,返回None的情况可能会使程序死掉,尽量用getOrElse
Option[+A]密封抽象类,Some和None继承Option类
14.17 偏函数
被包括在花括号内的一组case语句是一个偏函数——==并非对所有输入值都有定义的函数==。它是PartialFunction[A,B]类的一个实例,该类有两个方法:apply方法从匹配到的模式计算函数值,而isDefinedAt
方法在输入至少匹配其中一个模式时返回true
val f:PartialFunction[Char,Int] = {
case '+' => 1
case '-' => -1
}
println(f('+'))//1
//println(f('0'))//error
println(f.isDefinedAt('0'))//false