模式匹配和样例类
模式匹配
scala没有java的switch case语法,但是提供了 match case 模式匹配
不同点
switch case只能匹配变量的值,
match case可以匹配各种情况,变量的类型,集合的元素,有值或无值
match case 语法 变量 match {case 值 => 代码}
如果值为下划线,表示不满足以上所有情况下的处理
match case中只要一个case分支满足并处理了,就不会继续判断下一个case
更好的switch
scala的模式匹配只能匹配到一个分支,不需要break语句,因为他不会掉到下一个分支里
match是表达式,与if一样是有值的
类型匹配
scala模式匹配可以直接匹配类型,而不是值,
var arr = Array("hs",1,2.0.'a') var obj = arr(Random.nexInt(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对象 }
泛型的匹配
case m: Map[String,Int] => ... //可以匹配一个通用的映射 case m: Map[_,_] => ... case m: Array[Int] => ...
字符串问题
case "leetom" => ... case _ => ..
数组匹配
可以匹配待制定元素,带指定个数,以某个元素打头
case Array(0) => "0" //匹配包含0 的数组 case Array(x,y) => s"$x,$y" //匹配带有任意两个元素的数组,并将元素绑定到x和y case Array(0,_*) => "0.." //匹配任何以0开始的数组 case _ => "something else
列表匹配
case (0,_) => "0 .." case (y,0) => s"$y 0" case _ => "neither is 0"
样例类
在scala中是一种特殊类,样例类时不可变的
可以通过值进行比较,可用于模式匹配
定义一个样例类:
1.构造其中每一个参数都是val 除非显示的声明为var
2.伴生对象提供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) //Point(1,2) 和 Point(1,2)一样的 if (point == yetAnotherPoint) //Point(1,2)和Point(2,2)是不同的
在模式匹配中使用样例类:
样例类是一种特殊的类,可用于模式匹配,case class是多例的,后面要跟构造参数, case object是单例的
声明样例类,会发生以下情况
1.提供unapply方法,让模式匹配可以工作
2.生成toString equals hashCode copy方法,除非显示的给出这些方法的定义.
upapply提取器
match-case是可以匹配任何数据类型,自定义类也可以进行匹配
case中对象的upapply方法返回Some集合则代表这个对象匹配成功,反之如果返回none集合则匹配失败
总结
apply方法通常会被称为注入方法,(创建对象),在类的伴生对象中可以做些初始化的操作
apply方法的参数列表不需要和原生类的构造方法参数一致
unapply方法通常被提取方法,使用unapply方法可以提取固定数量的对象,或值
upapply方法返回的是一个option类型,因为unapply除了提取值之外还有一个目的就是判断对象是否创建成功
如果成功则有值,反之没有值,所以才使用option作为返回值类型
只要是object类,默认都是提供apply和unhappy方法,被默认隐式调用
option类型
在scala中option类型样例类用来表示可能存在或者也可能不存在的是(Option的子类有some和None some包装了某个值,none没有值
高阶函数
概念
如果一个函数的传入参数作为函数或者返回值是函数,则该函数即为高阶函数
传入参数为函数
scala中,函数头等公民,和数字一样,不仅可以调用,还可以在变量中存放函数,也可以作为参数传入函数,或者作为函数的返回值
arr.map(fun)
传入参数为匿名函数
在scala中你不需要给每一个函数命名,就像不必给每个数字命名一样,将函数赋值给变量的函数叫匿名函数
arr.map((x:Int)=>x*2) arr.map(x => x*2) arr.map(_*2)
传入参数为方法(隐式转换方法到函数
在scala中,方法和函数是不一样的,最本质的区别是函数可以作为参数传递到方法中
private def converCtoF(temp:Double) = temp * 1.8 +32 //方法converCtoF作为参数传入 def forecastInFahrenheit:Seq[Double] = temperatures.map(convertCtoF)
返回值为函数
def urlBuilder(ssl:Boolean,domainName:String):(String,String)=>String = { val schema = if (ssl) "https://" else "http://" (endpoint:String,query:String) => s"$chema$domainName/$endpoitn?$query" } val domainName = "www.example.com" def getURL = urlBuilder(ssl=true, domainName) val endpoint = "users" val query = "id=1" val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量
函数体内可以访问相应作用域内的任何变量
闭包通常来说可以简单的认为是可以访问一个函数里局部变量的另一个函数
普通函数:
val multiplier = (i:Int) => i*10
var factor = 3 val mulitiplier = (i:Int) => i*factor
引入一个自由变量factor这个变量定义在函数外面,
这样定义的函数变量multiplier称为一个闭包,因为他引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数
柯里化
Curring函数指的是,将原来接收两个参数的一个函数,转换为两个函数,第一个函数接收原先的第一个参数,然后返回接收原先第二个参数的第二个函数
函数调用过程中,就变为了两个函数连续调用的形式
当你调用curried(1)(2),实际上是一次调用两个普通函数(非柯里化函数),第一次调用使用一个参数x,返回一个函数类型的值,第二次使用参数y调用这个函数类型的值,
val onPlus = curriedSum(1)_
下划线作为第二参数列表的占位符,这个定义的返回值为一个函数,当调用时,会给调用的参数加一
偏函数
被包在花括号内没有match的一组case语句是一个偏函数,他是partialFunction[A,B]的一个实例,A代表参数类型,B表示返回值类型,常用作输入模式匹配,
一个是apply()方法,直接调用可以通过函数体内的case进行匹配,返回结果
另一个是isDefineAt()方法,可以返回一个输入,是否跟任何一个case语句匹配
隐式转换和隐式参数
概念
隐式转换和隐式参数是scala中两个强大的功能,利用隐式转换和隐式参数,你可以提供优雅的类库,对类库的使用者隐匿掉哪些枯燥乏味的细节
作用
隐式的对类的方法进行增强,丰富现有类库的功能
object ImplicitDemo extends App{ //定义隐式类,可以把File转换成定义的隐式类RichFile implicit class RichFile(form:File){ def read:String = Souce.fromFile(from.getPath).mkString } //使用隐式类做已有的类的动态的扩展, val contents = new File("src/test1.txt").read println(contents) }
隐式引用
scala会自己主动为每一个程序加上几个隐式引用,就像java程序会自己主动加上java.lan博爱一样
scala中,下面三个包的内容会隐式引用到每一个程序上,不同的是,
scala还会隐式加进队predef的引用
import java.lang._ import scala._ import Predef._
上面三个包,包括了经常使用的类型和方法,java.lang包括经常使用的java语言类型,假设在.net环境中,则会引用system命名空间,scala还会隐式引用scala包,就是引入经常使用的scala类型
隐式类
创建隐式类只需要在对应的类前加上implicit关键字
object Helpers{ implicit class InWithTimes(x:Int){ def times[A](f: =>A):Unit = { def loop(current:Int):Unit = if (current > 0){ f loop(current-1) } loop(x) } } }
这个例子创建了一个名为IntWithTimes的隐式类,这个了包含了一个Int值和一个名为times的方法,需要使用这个类,只需将其导入作用域内,并调用times方法
使用隐式类时,类名必须在当前作用域内可见,且无歧义,这一要求与隐式值等其他隐式类型转换方法类似
只能在别的trait/类/对象内部定义
object Helpers{ implicit class RichInt(x:Int)//正确 } implicit class RichDouble(x:Double)//正确
构造函数只能携带一个非隐式参数
implicit class RichDate(date:java.util.Date)//正确 implicit class Indexer[T](collecton:Seq[T],index:Int)//错误 implicit class Indexer[T](collecton:Seq[T])(implicit index:Index)//正确
虽然我们可以创建带有多个非隐式参数的隐式类,但是这些类无法用于隐式转换
在同一个作用域内不能有任何方法,成员或者对象与隐式类同名
object Bar implicit class Bar(x:Int)//错误 val x=5 implicit class x(y:Int)//错误 implicit case class Baz(x:Int)//错误
隐式转换函数
是指那种以implicit关键字声明的带有单个参数的函数,这种函数将被自动引用,将值从一种类型转换成另一种类型
使用隐含转换将变量转换成预期累心是编译器最先使用implicit的地方
这个规则非常简单,当编译器看到类型x而需要的类型却是y,他就在当前作用域查找是否定义了从类型x到类型y的隐式含义
隐式参数
在函数定义的时候,支持在最后一组使用implicit 表明这是一组隐式参数,在调用该函数的时候,可以不用传递隐式函数,而编译器会自动寻找一个implict标记过的合适的值作为该参数
泛型
泛型提高了某些类或方法的灵活性
带有一个或者多个类型参数的类时泛型的
[B <: A] upper bounds 上限或上界 B类型的上界是A类型,也就是B类型的父类是A类型 [B >: A] lower bounds 下界 B类型的下界是A类型,B的子类是A [B <% A] view bounds 视界 表示B转换成A需要一个隐式转换函数 [B : A] context bounds 上下文规定(用到了隐式参数的语法糖)需要一个隐式转换的值 [ -A ] 逆变 作为参数类型,是指实现的参数类型是接口定义的参数类型的父类 [ +B ] 协变 作为返回类型,是指返回累心是接口定义返回类型的子类