Kotlin学习(十七)—— 高阶函数和lambda表达式

高阶函数

高阶函数是将函数用作参数或返回值的函数。这种函数的⼀个很好的例⼦是 lock() ,它接受⼀个锁对象和⼀个函数,获取锁,运行函数并释放锁
get一下上面的信息
1. 函数能作为参数传入一个函数
2. 函数能作为返回值,声明。
看下面的代码:

fun  lock(lock:Lock, body :() -> T):T{
    lock.lock()
    try {
       return body()
    }finally {
        lock.unlock()
    }
}
/*让我们来检查上⾯的代码:body 拥有函数类型:() -> T,所以它应该是⼀个不带参数并且返回 T 类型值的函数。
它在 try-代码块内部调⽤、被lock 保护,其结果由 lock() 函数返回。*/
/*如果我们想调⽤ lock() 函数,我们可以把另⼀个函数传给它作为参数,
还可以传入一个lambda表达式(这里暂时不解释lambda表达式是什么,先看看怎么使用)*/
fun main(args: Array) {
    fun opreation() = "hello"  //定义一个局部函数,传入lock()作为参数
    println(lock(ReentrantLock(),::opreation))// 要使用函数作为参数,那么要使用::来引用
    println(lock(ReentrantLock() ,{"hello"}))//传入一个lambda表达式

}

lambda表达式简述

  1. lambda 表达式总是被⼤括号括着,
  2. 其参数(如果有的话)在 -> 之前声明(参数类型可以省略),
  3. 函数体(如果存在的话)在 -> 后⾯。
fun main(args: Array) {
    /*在 Kotlin 中有⼀个约定,如果函数的最后⼀个参数是⼀个函数!!!,
    并且你传递⼀个 lambda 表达式作为相应的参数,你可以在圆括号之外指定它.*/
    //上面的lock函数可以这样
    //调用上面的lock()函数,lambda表达式被写在了括号外面
    println(lock(ReentrantLock()){  
        "hello"
    })
}

另一个高阶函数是map

fun  List.map(transForm: (T) -> R): List{
    val result = arrayListOf()
    for (item in this){
        result.add(transForm(item))
    }
    return result
}
fun main(args: Array) {
    val l = listOf(1, 2, 3, 4, 5)
    //该函数可以如下调⽤
    //请注意,如果 lambda 是该调⽤的唯⼀参数,则调⽤中的圆括号可以完全省略,如下
    val l1 = l.map { value -> value * 2 }
    //另⼀个有⽤的约定是,如果函数字⾯值只有⼀个参数,那么它的声明可以省略(连同 -> ),其名称是 it 。
    val l2 = l.map { it * 2 }
    println(l1)
    println(l2)
    //上面的这些约定可以写LINQ-⻛格的代码:
    val strings = arrayOf("hello", "very", "fine", "bother", "linaok", "happy")
    val newStrings = strings.filter { it.length >= 5 }.sortedBy { it.length }.map { it.toUpperCase() }
    println(newStrings)
    //如果 lambda 表达式的参数未使⽤,那么可以⽤下划线取代其名称
    var map = mapOf("one" to 1,"two" to 2,"three" to 3)
    println(map.forEach { _, u -> println("$u") })
}

Lambda表达式和匿名函数

⼀个 lambda 表达式或匿名函数是⼀个“函数字⾯值”,即⼀个未声明的函数,但⽴即做为表达式传递。考虑下⾯的例⼦

fun main(args: Array) {
    //⼀个 lambda 表达式或匿名函数是⼀个“函数字⾯值”,即⼀个未声明的函数,
    //但⽴即做为表达式传递。考虑下⾯的例⼦
    val strings = listOf("I","love","you","destination")
    //函数 max 是⼀个⾼阶函数,换句话说它接受⼀个函数作为第⼆个参数。
    //其第⼆个参数是⼀个表达式,它本⾝是⼀个函数,即函数字⾯值。返回的是一个Int类型的返回值
    println(max(strings,{
        a,b -> if (a.length > b.length) 1 else 0
    }))

}

函数类型

对于接受另⼀个函数作为参数的函数,我们必须为该参数指定函数类型。例如上述函数 max 定义如下:

fun  max(collection: Collection, less: (T, T) -> Boolean): T? {
    var max: T? = null
    for (it in collection)
        if (max == null || less(max, it))
            max = it
    return max
}

上面代码中参数 less 的类型是 (T, T) -> Boolean,即⼀个接受两个类型 T 的参数并返回⼀个布尔值的函数:如果第⼀个参数小于第⼆个那么该函数返回 true。
在上⾯第 4 行代码中,less 作为⼀个函数使⽤:通过传⼊两个 T 类型的参数来调用。

如上所写的是就函数类型,或者可以有命名参数,如果你想⽂档化每个参数的含义的话。

val compare : (x: T,y: T)->Int = ...

如要声明⼀个函数类型的可空变量,请将整个函数类型括在括号中并在其后加上问号

var sum : ((a:Int ,b:Int)->Int)? =null

ambda表达式完整语法,定义一个函数类型

val sum = {x:Int,y:Int -> x + y}

lambda 表达式总是被⼤括号括着,完整语法形式的参数声明放在括号内,并有可选的类型标注,函数体跟在⼀个 -> 符号之后。如果推断出的该lambda 的返回类型不是 Unit ,那么该 lambda 主体中的最后⼀个(或可能是单个)表达式会视为返回值
如果我们把所有可选标注都留下,看起来如下 ,sum:后面“(Int ,Int)->Int”是函数的类型,表示有两个参数,返回值为Int类型 = 后面lambda表达式

//x,y 是参数的名字,x+y是一个表达式,返回的就是x+y的值
val sum :(Int ,Int)->Int = {x, y -> x + y}
fun main(args: Array<String>) {
    var a = 1
    var b = 2
    //调用函数的引用
    var c = sum(a,b)
    println(c)
}

⼀个 lambda 表达式只有⼀个参数是很常⻅的。如果 Kotlin 可以⾃⼰计算出签名,它允许我们不声明唯⼀的参数,并且将隐含地为我们声明其名称为it

fun main(args: Array) {
    var arr = arrayOf(1,2,3,4,5,6)
    //上面的代码和下面的代码等价
    var arr1 = arr.filter {
        val shouldFilter = it > 2
        shouldFilter
    }
    //也和下面的代码等价
    var arr2 = arr.filter {
        val shouldFilter = it > 2
        //@filter代表的是filter函数的返回值
        return@filter shouldFilter
    }
    //请注意,如果⼀个函数接受另⼀个函数作为最后⼀个参数,lambda 表达式参数可以在圆括号参数列表之外传递。
    //参⻅ callSuffix 的语法
    println("$arr $arr1 $arr2")
}

lambda表达式还能返回Unit的类型的返回值

fun main(args: Array<String>) {
    //lambda表达式还能返回Unit的类型的返回值
    var sum : (Int,Int)->Unit = { x,y ->
        println(x+y)
    }
    sum(1,2)
}

匿名函数

上⾯提供的 lambda 表达式语法缺少的⼀个东西是指定函数的返回类型的能力。在大多数情况下,这是不必要的。因为返回类型可以自动推断出来。然而,如果确实需要显式指定,可以使用另⼀种语法:匿名函数

var sum = fun(x :Int,y :Int):Int = x + y
fun main(args: Array) {
    println(sum(11,3))
    var arr = arrayOf(1,2,3,4,5,6)
    //匿名函数可以像以下方式使用
    var arr1 = arr.filter(fun(it):Boolean = it >2 )//这里传入的是一个匿名函数
    var arr2 = arr.filter(fun(it) = it > 2)
}

匿名函数的返回类型推断机制与正常函数⼀样:对于具有表达式函数体的匿名函数将自动推断返回类型,而具有代码块函数体的返回类型必须显式指定(或者已假定为 Unit )。
请注意,匿名函数参数总是在括号内传递。允许将函数留在圆括号外的简写语法仅适用于 lambda 表达式

Lambda表达式与匿名函数之间的另⼀个区别是非局部返回的行为。⼀个不带标签的 return 语句总是在用 fun 关键字声明的函数中返回。这意味着lambda 表达式中的 return 将从包含它的函数返回,而匿名函数中的 return 将从匿名函数自身返回。
get一下上面的点
1. lambda表示式要从表达式返回要用@
2. lambda表达式返回不是非局部返回的,默认是从包裹它的函数返回的
2. 匿名函数返回是函数内部返回

闭包

Lambda 表达式或者匿名函数(以及局部函数和对象表达式)可以访问其 闭包 ,即在外部作用域中声明的变量。与 Java 不同的是可以修改闭包中捕获的变量

fun main(args: Array) {
    var arr = arrayOf(1,2,3,4,5,6)
    var sum = 0
    //lambda表达式访问main的闭包中变量
    arr.filter { it > 0 }.forEach { sum += it }
    println(sum)
    //匿名函数访问main的闭包中变量
    sum = 0
    arr.filter(fun(it):Boolean = it > 0).forEach(fun(it){
        sum += it
    })
    println(sum)
    //局部函数访问main的闭包中变量
    sum = 0
    fun sum(){
        for (i in arr){
            if (i >0){
                sum += i
            }
        }
    }
    sum()
    println(sum)
    //对象表达式访问main的闭包中变量
    sum = 0
    var result  = object  {
        fun sum(){
            for (i in arr){
                if (i >0){
                    sum += i
                }
            }
        }
    }
    result.sum()
    println(sum)
}

相信上面的代码能说明闭包的概念了

带接收者的函数字面值

Kotlin 提供了使用指定的接收者对象调用函数字面值的功能。在函数字面值的函数体中,可以调用该接收者对象上的方法而无需任何额外的限定符。这类似于扩展函数,它允许你在函数体内访问接收者对象的成员。其用法的最重要的示例之⼀是类型安全的 Groovy-风格构建器。

fun  getSum(array : Array<Int>, sum :(a : Int,b :Int)->Int):Int{
    var sum = 0
    for (i in array){
        //使用sum来接收函数参数
        sum = sum(sum,i)
    }
    return sum
}
val sum = fun Int.(other :Int) : Int = this + other  //这个函数的接受者就是Int


fun main(args: Array<String>) {
    println(1.sum(2))

    var array = arrayOf(1,2,3,4,5,7,0)
    //lambda表达式调用字面值 如我们调用filter()函数一样,可以省略小括号直接filter{}
    var sum = getSum(array){ a,b -> a + b }
    println(sum)
    var sum1 = getSum(array,fun(a: Int ,b :Int):Int {
        return a+b
    })
    println(sum1)
}

匿名函数语法允许你直接指定函数字面值的接收者类型。如果你需要使用带接收者的函数类型声明⼀个变量,并在之后使用它,这将非常有用。

val sum = fun Int.(other: Int): Int = this + other  //这个匿名函数的接受者是Int类型

当接收者类型可以从上下⽂推断时,lambda 表达式可以⽤作带接收者的函数字⾯值。

class Html(){
    fun body(){
        println("这是网页的主体")
    }
}
//下面的函数的接受者类型是Html。函数的名称是init。
fun html(init:Html.()->Unit):Html{
    val html = Html() // 创建接收者对象
    html.init() // 将该接收者对象传给该 lambda
    return html
}

fun main(args: Array) {
    html {
        //lambda表达时候充当了字面值
        body()
    }
}

你可能感兴趣的:(kotlin语言)