03Scala高级特性.docx
《03Scala高级特性.docx》由会员分享,可在线阅读,更多相关《03Scala高级特性.docx(10页珍藏版)》请在冰豆网上搜索。
03Scala高级特性
Scala高级特性
1.课程目标
1.1.目标一:
深入理解高阶函数
1.2.目标一:
深入理解隐式转换
2.高阶函数
2.1.概念
Scala混合了面向对象和函数式的特性,我们通常将可以做为参数传递到方法中的表达式叫做函数。
在函数式编程语言中,函数是“头等公民”,高阶函数包含:
作为值的函数、匿名函数、闭包、柯里化等等。
2.2.作为值的函数
可以像任何其他数据类型一样被传递和操作的函数,每当你想要给算法传入具体动作时这个特性就会变得非常有用。
valarr=(1to10).toArray
valfunc1=(x:
Int)=>x*2
arr.map(func1)
valarr=(1to10)
valfunc1=(x:
Int)=>x*2
arr.map(func1)
定义函数时格式:
val变量名=(输入参数类型和个数)=>函数实现和返回值类型和个数
“=”表示将函数赋给一个变量
“=>”左面表示输入参数名称、类型和个数,右边表示函数的实现和返回值类型和参数个数
2.3.匿名函数
在Scala中,你不需要给每一个函数命名,没有将函数赋给变量的函数叫做匿名函数
由于Scala可以自动推断出参数的类型,所有可以写的跟精简一些
还记得神奇的下划线吗?
这才是终极方式
valarr=(1to10).toArray
arr.map((x:
Int)=>x*2)
arr.map(x=>x*2)
arr.map(words=>words*2)
arr.map(counts=>counts*2)
arr.map(_*2)
占位符只能出现一次:
valstr=Array("hello","world","java","c","scala","spark","hdfs","mapreduce")
str.map(_.length*_length)
str.map(x=>x.length*x.length)
str.map(scala.math.pow(_.length,2))
str.map(x=>scala.math.pow(x.length,2))
备注:
初学者少用占位符。
如果一定要用,可以在程序功能实现后,再返回去考虑是否可以将变量替换为占位符。
一些情况下,我们的程序逻辑是没有问题的,可能只是语法上有点小问题。
2.4.将方法转换成函数
在Scala中,方法和函数是不一样的,最本质的区别是函数可以做为参数传递到方法中
但是方法可以被转换成函数,神奇的下划线又出场了
区别没有这么大!
valarr=(1to10).toArray
defm(x:
Int)=x*3
valfunc2=m_
arr.map(func2)
arr.map(m)
2.5.柯里化
柯里化指的是将原来接受两个参数的方法变成新的接受一个参数的方法的过程
defm1(x:
Int)(y:
Int)=x*y
valfunc1=m1(3)_
func1(5)
defm2(x:
Int)=(y:
Int)=>x+y
valfunc2=m2(3)
func2(5)
语法上有一点小区别!
2.6.例子
packagecn.itxdl.scala
objectFunDemo{
defmain(args:
Array[String]){
deff2(x:
Int)=x*2
valf3=(x:
Int)=>x*3
valf4:
(Int)=>Int={x=>x*4}
valf4a:
(Int)=>Int=_*4
valf5=(_:
Int)*5
vallist=List(1,2,3,4,5)
defm1(x:
Int)(y:
Int)=x*y
defm2(x:
Int)=(y:
Int)=>x*y
//第一种:
最直观的方式(Int)=>Int
//new_list=list.map((x:
Int)=>x*3)
//第二种:
由于map方法知道你会传入一个类型为(Int)=>Int的函数,你可以简写
//new_list=list.map((x)=>x*3)
//第三种:
对于只有一个参数的函数,你可以省去参数外围的()
//new_list=list.map(x=>x*3)
//第四种:
(终极方式)如果参数在=>右侧只出现一次,可以使用_
new_list=list.map(_*3)
new_list.foreach(println(_))
vara=Array(1,2,3)
a.map(_*3)
}
}
3.隐式转换和隐式参数
隐式转换、隐式转换函数、隐式类、隐式对象、隐式参数、隐式值
3.1.概念
隐式转换和隐式参数是Scala中两个非常强大的功能,利用隐式转换和隐式参数,你可以提供优雅的类库,对类库的使用者隐匿掉那些枯燥乏味的细节。
备注:
高级技能,我们实际编程中用的相对不多,主要用在类库设计中。
如果要阅读、开发类库,此项技能必须掌握。
在scala语言当中,隐式转换是一项强大的程序语言功能,它不仅能够简化程序设计,也能够使程序具有很强的灵活性。
要想更进一步地掌握scala语言,了解其隐式转换的作用与原理是很有必要的。
隐式转换是无处不在的,只不过scala语言为我们隐藏了相应的细节,例如scala中的类继承层次结构中:
3.2.作用
隐式的对类的方法进行增强,丰富现有类库的功能
3.3.隐式转换函数
是指那种以implicit关键字声明的带有单个参数的函数
以下语句会报错:
valx:
Int=3.5
添加隐式转换函数后可以实现Double类型到Int类型的赋值
implicitdefdouble2Int(x:
Double)=x.toInt
valx:
Int=3.5
隐式函数的名称对结构没有影响,即函数名double2Int可以是任何名字,这种方式命名的意思比较明确,阅读代码的人可以见名知义,增加代码的可读性。
implicitdefa(x:
Double)=x.toInt
valx:
Int=3.5
这种命名方式显然不可取!
隐式转换功能十分强大,可以快速地扩展现有类库的功能,例如下面的代码。
3.4.隐式转换例子
利用隐式转换丰富现有类库的功能
packagecn.itxdl.impli
importjava.io.File
importscala.io.Source
//隐式的增强File类的方法
classRichFile(valfrom:
File){
defread=Source.fromFile(from.getPath).mkString
}
objectRichFile{
//隐式转换方法
implicitdeffile2RichFile(from:
File)=newRichFile(from)
}
objectMainApp{
defmain(args:
Array[String]):
Unit={
//导入隐式转换
importRichFile._
//importRichFile.file2RichFile
println(newFile("c:
//a.txt").read)
}
}
packagecn.itxdl.scala
importjava.awt.GridLayout
objectImplicitContext{
//implicitdefgirl2Ordered(g:
Girl)=newOrdered[Girl]{
//overridedefcompare(that:
Girl):
Int=if(g.faceValue>that.faceValue)1else-1
//}
implicitobjectOrderingGirlextendsOrdering[Girl]{
overridedefcompare(x:
Girl,y:
Girl):
Int=if(x.faceValue>y.faceValue)1else-1
}
}
classGirl(varname:
String,varfaceValue:
Double){
overridedeftoString:
String=s"name:
$name,faveValue:
$faceValue"
}
//classMissRight[T<%Ordered[T]](f:
T,s:
T){
//defchoose()=if(f>s)felses
//}
//classMissRight[T](f:
T,s:
T){
//defchoose()(implicitord:
T=>Ordered[T])=if(f>s)felses
//}
classMissRight[T:
Ordering](valf:
T,vals:
T){
defchoose()(implicitord:
Ordering[T])=if(ord.gt(f,s))felses
}
objectMissRight{
defmain(args:
Array[String]){
importImplicitContext.OrderingGirl
valg1=newGirl("yuihatano",99)
valg2=newGirl("jzmb",98)
valmr=newMissRight(g1,g2)
valresult=mr.choose()
println(result)
}
}
Scala会考虑如下的隐式转换规则:
1、位于源或目标类型的伴生对象中的隐式函数
2、位于当前作用域可以以单个标示符指代的隐式函数
3.5.隐式参数
隐式参数与我们前面讲述的隐式类型转化有很大的差异,虽然它涉及到了关键字implict,但是它做的是另外一件事情。
隐式参数有点类似缺省参数,如果在调用方法时没有提供某个参数,编译器会在当前作用域查找是否有符合条件的implicit对象可以作为参数传入,不同于缺省参数,隐式参数的值可以在方法调用前的上下文中指定,这是隐式参数更加灵活的地方。
函数或方法可以带有一个标记为implicit的参数列表。
这种情况下,编译器将会查找缺省值,提供给该函数或方法。
//隐式参数的例子
objectcontext{
implicitvalaa="guanyu"
}
objectImpli{
defsay()(implicitname:
String="zhangfei"):
Unit={
println(s"hello$name")
}
defmain(args:
Array[String]):
Unit={
importcontext._
say()("liubei")
}
}
备注:
对于给定的数据类型,只能有一个隐式值。
packagecn.itxdl.scala
objectImplicitParamDemo{
objectGreeter{
//隐式参数,有点类似于缺省参数
defgreet(name:
String)(implicitprompt:
String){
println("Welcome,"+name+".TheSystemisready.")
println(prompt)
}
}
defmain(args:
Array[String]){
implicitvalprompt=">>>"
Greeter.greet("admin")
}
}
备注:
高级特性尽量少用!
引入的原因是scala中的某些特性比较难懂或有些晦涩,或有些特性是试验性质。
这些特性在普通场景下不鼓励使用,但在特定场景下又可能需要。
所以引入了“显式引入语言特性”的做法。
举例来说,在2.10的repl下定义隐式转换:
使用scala–feature重新启动:
这回给出了详细的警告信息,使用隐式转换方法,建议在代码中引入language单例的implicitConversions成员:
importscala.language.implicitConversions或者编译时指定-language:
implicitConversions参数。
为什么要控制隐式转换这一特性(要显式启用)?
是因为过度使用隐式转换会有很多陷阱,而现在已经有这个趋势,因为隐式转换的强大而被过度使用。
另外,相对隐式转换,采用隐式参数对设计更好一些。
类似问题还有:
另外不鼓励的特性还有(对普通开发者而言):
存在类型(existentials)、高阶类(higherKinds)、动态类(dynamics)、反射调用(reflectiveCalls)