1、03Scala高级特性Scala高级特性1. 课程目标1.1. 目标一:深入理解高阶函数1.2. 目标一:深入理解隐式转换2. 高阶函数2.1. 概念Scala混合了面向对象和函数式的特性,我们通常将可以做为参数传递到方法中的表达式叫做函数。在函数式编程语言中,函数是“头等公民”,高阶函数包含:作为值的函数、匿名函数、闭包、柯里化等等。2.2. 作为值的函数可以像任何其他数据类型一样被传递和操作的函数,每当你想要给算法传入具体动作时这个特性就会变得非常有用。val arr = (1 to 10).toArrayval func1 = (x:Int) = x * 2arr.map(func1)v
2、al arr = (1 to 10)val func1 = (x:Int) = x * 2arr.map(func1)定义函数时格式:val 变量名 = (输入参数类型和个数) = 函数实现和返回值类型和个数“=”表示将函数赋给一个变量“=”左面表示输入参数名称、类型和个数,右边表示函数的实现和返回值类型和参数个数2.3. 匿名函数在Scala中,你不需要给每一个函数命名,没有将函数赋给变量的函数叫做匿名函数由于Scala可以自动推断出参数的类型,所有可以写的跟精简一些还记得神奇的下划线吗?这才是终极方式val arr = (1 to 10).toArrayarr.map(x:Int) = x
3、 * 2)arr.map(x=x*2)arr.map(words=words*2)arr.map(counts=counts*2)arr.map(_*2)占位符只能出现一次:val str = 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)备注:初学者少用占位符。如果一定要
4、用,可以在程序功能实现后,再返回去考虑是否可以将变量替换为占位符。一些情况下,我们的程序逻辑是没有问题的,可能只是语法上有点小问题。2.4. 将方法转换成函数在Scala中,方法和函数是不一样的,最本质的区别是函数可以做为参数传递到方法中但是方法可以被转换成函数,神奇的下划线又出场了区别没有这么大!val arr = (1 to 10).toArraydef m(x:Int) = x * 3val func2 = m _arr.map(func2)arr.map(m)2.5. 柯里化柯里化指的是将原来接受两个参数的方法变成新的接受一个参数的方法的过程def m1(x:Int)(y:Int) =
5、 x * yval func1 = m1(3) _func1(5)def m2(x:Int) = (y:Int) = x + yval func2 = m2(3)func2(5)语法上有一点小区别!2.6. 例子package cn.itxdl.scalaobject FunDemo def main(args: ArrayString) def f2(x: Int) = x * 2 val f3 = (x: Int) = x * 3 val f4: (Int) = Int = x = x * 4 val f4a: (Int) = Int = _ * 4 val f5 = (_: Int) *
6、 5 val list = List(1, 2, 3, 4, 5)def m1(x:Int)(y:Int) = x*ydef m2(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) /第四种:(终极方式
7、)如果参数在=右侧只出现一次,可以使用_ new_list = list.map(_ * 3) new_list.foreach(println(_) var a = Array(1,2,3) a.map(_* 3) 3. 隐式转换和隐式参数隐式转换、隐式转换函数、隐式类、隐式对象、隐式参数、隐式值3.1. 概念隐式转换和隐式参数是Scala中两个非常强大的功能,利用隐式转换和隐式参数,你可以提供优雅的类库,对类库的使用者隐匿掉那些枯燥乏味的细节。备注:高级技能,我们实际编程中用的相对不多,主要用在类库设计中。如果要阅读、开发类库,此项技能必须掌握。在scala语言当中,隐式转换是一项强大的程
8、序语言功能,它不仅能够简化程序设计,也能够使程序具有很强的灵活性。要想更进一步地掌握scala语言,了解其隐式转换的作用与原理是很有必要的。隐式转换是无处不在的,只不过scala语言为我们隐藏了相应的细节,例如scala中的类继承层次结构中:3.2. 作用隐式的对类的方法进行增强,丰富现有类库的功能3.3. 隐式转换函数是指那种以implicit关键字声明的带有单个参数的函数以下语句会报错:val x:Int=3.5添加隐式转换函数后可以实现Double类型到Int类型的赋值implicit def double2Int(x:Double)=x.toIntval x:Int=3.5隐式函数的名
9、称对结构没有影响,即函数名double2Int可以是任何名字,这种方式命名的意思比较明确,阅读代码的人可以见名知义,增加代码的可读性。implicit def a(x:Double)=x.toIntval x:Int=3.5这种命名方式显然不可取!隐式转换功能十分强大,可以快速地扩展现有类库的功能,例如下面的代码。3.4. 隐式转换例子利用隐式转换丰富现有类库的功能package cn.itxdl.impliimport java.io.Fileimport scala.io.Source/隐式的增强File类的方法class RichFile(val from: File) def read
10、 = Source.fromFile(from.getPath).mkStringobject RichFile /隐式转换方法 implicit def file2RichFile(from: File) = new RichFile(from)object MainApp def main(args: ArrayString): Unit = /导入隐式转换 import RichFile._ /import RichFile.file2RichFile println(new File(c:/a.txt).read) package cn.itxdl.scalaimport java.a
11、wt.GridLayoutobject ImplicitContext /implicit def girl2Ordered(g : Girl) = new OrderedGirl / override def compare(that: Girl): Int = if (g.faceValue that.faceValue) 1 else -1 / implicit object OrderingGirl extends OrderingGirl override def compare(x: Girl, y: Girl): Int = if (x.faceValue y.faceValue
12、) 1 else -1 class Girl(var name: String, var faceValue: Double) override def toString: String = sname : $name, faveValue : $faceValue/class MissRightT s) f else s/class MissRightT(f: T, s: T)/ def choose()(implicit ord: T = OrderedT) = if (f s) f else s/class MissRightT: Ordering(val f: T, val s: T)
13、 def choose()(implicit ord: OrderingT) = if(ord.gt(f, s) f else sobject MissRight def main(args: ArrayString) import ImplicitContext.OrderingGirl val g1 = new Girl(yuihatano, 99) val g2 = new Girl(jzmb, 98) val mr = new MissRight(g1, g2) val result = mr.choose() println(result) Scala会考虑如下的隐式转换规则:1、位
14、于源或目标类型的伴生对象中的隐式函数2、位于当前作用域可以以单个标示符指代的隐式函数3.5. 隐式参数隐式参数与我们前面讲述的隐式类型转化有很大的差异,虽然它涉及到了关键字implict,但是它做的是另外一件事情。隐式参数有点类似缺省参数,如果在调用方法时没有提供某个参数,编译器会在当前作用域查找是否有符合条件的 implicit 对象可以作为参数传入,不同于缺省参数,隐式参数的值可以在方法调用前的上下文中指定,这是隐式参数更加灵活的地方。函数或方法可以带有一个标记为implicit的参数列表。这种情况下,编译器将会查找缺省值,提供给该函数或方法。/ 隐式参数的例子object context
15、 implicit val aa = guanyuobject Impli def say() (implicit name:String = zhangfei): Unit = println(shello $name) def main(args: ArrayString): Unit = import context._ say()(liubei) 备注:对于给定的数据类型,只能有一个隐式值。package cn.itxdl.scalaobject ImplicitParamDemo object Greeter / 隐式参数,有点类似于缺省参数 def greet(name:Strin
16、g)(implicit prompt: String) println(Welcome, + name + . The System is ready.) println(prompt) def main(args: ArrayString) implicit val prompt = Greeter.greet(admin) 备注:高级特性尽量少用!引入的原因是scala中的某些特性比较难懂或有些晦涩,或有些特性是试验性质。这些特性在普通场景下不鼓励使用,但在特定场景下又可能需要。所以引入了“显式引入语言特性”的做法。举例来说,在2.10的repl下定义隐式转换:使用scala featur
17、e重新启动:这回给出了详细的警告信息,使用隐式转换方法,建议在代码中引入language单例的implicitConversions成员: import scala.language.implicitConversions 或者 编译时指定-language:implicitConversions参数。为什么要控制隐式转换这一特性(要显式启用)?是因为过度使用隐式转换会有很多陷阱,而现在已经有这个趋势,因为隐式转换的强大而被过度使用。另外,相对隐式转换,采用隐式参数对设计更好一些。类似问题还有:另外不鼓励的特性还有(对普通开发者而言):存在类型(existentials)、高阶类(higherKinds)、动态类(dynamics)、反射调用(reflectiveCalls)
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1