一.高阶函数
1.1概念
Scala混合了面向对象和函数式的特性,我们通常将可以作为参数传递到方法中的表达式(函数)叫做高阶函数。在函数式编程语言中,函数式“头等公民”,高阶函数包含:作为值的函数、匿名函数、闭包、柯里化等等。
1.2作为值的函数
可以向任何其它数据类型一样被传递和操作的函数,每当你想要给算法传入具体动作时这个特性就会变得非常有用。
定义函数时:val 变量名=(输入参数类型和个数) => 函数实现和返回值类型和个数
"="表示将函数赋给一个变量
"=>"左边表示输入参数名称、类型和个数,右边表示函数的实现和返回值类型和参数个数
1.3匿名函数
在Scala中,你不需要给每一个函数命名,没有将函数赋给变量的函数叫作匿名函数
由于Scala可以自动推断出参数的类型,所以可以写的精简一些
还记得神奇的下划线吗?这才是终极方式
1.4将方法转换成函数
在Scala中,方法和函数是不一样的,最本质的区别是函数可以作为参数传递到方法中,但是方法可以被转换成函数,神奇的下划线有出场了
1.5柯里化
柯里化指的是将原来接受两个参数的方法变成新的接受一个参数的方法的过程。
实例
object FunDemo {
def main(args: Array[String]) {
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) * 5
val list = List(1, 2, 3, 4, 5)
var new_list: List[Int] = null
//第一种:最直观的方式 (Int) => Int
new_list1 = list.map((x: Int) => x * 3)
//第二种:由于map方法知道你会传入一个类型为(Int) => Int的函数,你可以简写
new_list2 = list.map((x) => x * 3)
//第三种:对于只有一个参数的函数,你可以省去参数外围的()
new_list3 = list.map(x => x * 3)
//第四种:(终极方式)如果参数在=>右侧只出现一次,可以使用_
new_list4 = list.map(_ * 3)
new_list.foreach(println(_))
var a = Array(1,2,3)
a.map(_* 3)
}
}
二.隐式转换和隐式参数
2.1概念
隐式转换和隐式参数是Scala中两个非常强大的功能,利用隐式转换和隐识参数,你可以提供优雅的类库,对类库的使用者隐匿调那些枯燥的细节。
2.2作用
隐式的对类的方法进行增强,丰富现有类库的功能。
2.3隐式转换函数
是指那种以implicit关键字声明的带有单个参数的函数
2.4隐式转换例子
示例1
package com.zhiyoulxj.actor.implicitdemo
/** implicit 隐式
* 隐式转换值:
* 1.如果门面里没有定义参数类型的值,这个参数就是隐式值的值 (implicit name:String="zhangsan")
* 2.如果门面里有值,则参数值是相应的值(参数类型也要对应)
* 3.即使门面里有值,以方法调用时传进的参数为准 Hello()("wangwu")
*/
object Context{ //门面
implicit val bbb = "lisi"
implicit val k = 2
}
object MyContext {
def Hello()(implicit name:String="zhangsan")={
println("Hi" + name)
}
def main(args: Array[String]): Unit = {
import Context._
// Hello() 结果为 HiLisi
Hello()("wangwu") //结果为Hiwangwu
}
}
示例2
package com.zhiyoulxj.actor.implicitdemo
import java.io.File
import scala.io.Source
/**
* Java的装饰模式:增强原先的方法
* @param f
*/
class RichFile(val f:File) {
def read() = Source.fromFile(f).mkString
}
object RichFile{
def main(args: Array[String]): Unit = {
val f = new File("D://words.txt")
// val content = f.read()
val content = new RichFile(f).read()
println(content)
}
}
示例3
import java.io.File
import scala.io.Source
//隐式的增强File类的方法
class RichFile(val from: File) {
def read = Source.fromFile(from.getPath).mkString
}
object RichFile {
//隐式转换方法
implicit def file2RichFile(from: File) = new RichFile(from)
}
object MainApp{
def main(args: Array[String]): Unit = {
//导入隐式转换
import RichFile._
//import RichFile.file2RichFile
println(new File("c://words.txt").read)
}
}
示例4
/**
* Created by ZX on 2015/11/13.
*/
object ImplicitContext{
//implicit def girl2Ordered(g : Girl) = new Ordered[Girl]{
// override def compare(that: Girl): Int = if (g.faceValue > that.faceValue) 1 else -1
//}
implicit object OrderingGirl extends Ordering[Girl]{
override def compare(x: Girl, y: Girl): Int = if (x.faceValue > y.faceValue) 1 else -1
}
}
class Girl(var name: String, var faceValue: Double){
override def toString: String = s"name : $name, faveValue : $faceValue"
}
//class MissRight[T <% Ordered[T]](f: T, s: T){
// def choose() = if(f > s) f else s
//}
//class MissRight[T](f: T, s: T){
// def choose()(implicit ord: T => Ordered[T]) = if (f > s) f else s
//}
class MissRight[T: Ordering](val f: T, val s: T){
def choose()(implicit ord: Ordering[T]) = if(ord.gt(f, s)) f else s
}
object MissRight {
def main(args: Array[String]) {
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)
}
}
隐式函数的使用
package com.zhiyoulxj.actor.implicitdemo
import java.io.File
/**
* 隐式函数的使用
*/
object MyPredef{
//隐式转换 File--->RichFile
implicit def toRichFile(f:File)= new RichFile(f)
}
object ImpFunc {
def main(args: Array[String]): Unit = {
val f = new File("D://words.txt")
import MyPredef.toRichFile
val content = f.read()
println(content)
}
}
三.泛型
[T <: UpperBound] 上界 extends(java)
[T >: LowerBound] 下限 super
[T <% ViewBound] 视图界定
[T : ContextBound] 上下文界定
[+T] 协变
[-T] 逆变
在scala中定义一个特征函数:
trait Function1[-T +U] {
def apply(x: T): U
}
这种函数接受一个参数,参数类型为泛型类型T,返回类型为泛型类型U。和其他支持泛型的语言一样,实际定义函数时T和U的类型会被确定下来,不过需要注意的是,这边的T之前有一个"-",而U之前有一个"+"。
在这里引入关于这个符号的说明,在声明Scala的泛型类型时,“+”表示协变,而“-”表示逆变
C[+T]:如果A是B的子类,那么C[A]是C[B]的子类。 协变
C[-T]:如果A是B的子类,那么C[B]是C[A]的子类。 逆变
C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。 不变
根据Liskov(里式)替换原则,如果A是B的子类,那么能适用于B的所有操作,都适用于A。
假设Bird是Animal的子类,那么看看下面两个函数之间是什么关系:
def f1(x: Bird): Animal // instance of Function1[Bird, Animal]
def f2(x: Animal): Bird // instance of Function1[Animal, Bird]
在这里f2的类型时f1的类型的子类。为什么?
1.先看参数类型:根据Liskov替换原则,f1能够接受的参数,f2也能接受。在这里f1接受的Bird类型,f2显然可以接受,因为Bird对象可以被当做父类Animal的对象来使用。
2.再看返回类型:f1的返回值可以被当做Animal的实例使用,f2的返回值可以被当做Bird的实例使用,当然也可以被当做Animal的实例来使用。
3.所以我们说,函数的参数类型时逆变的,而函数的返回类型时协变的。
协变:可以理解为:儿子换老子 C[+T]
逆变:可以理解为:老子换儿子 C[-T]
trait Function1[-T,+U] | trait Function1[+T,-U] |
Function[String,Any] | Function[Any,String] |
Function[Any,String] | Function[String,Any] |