Kotlin语法(十八)-高阶函数和Lambda表达式

时间:2021-02-20 19:11:55

         参考原文:http://kotlinlang.org/docs/reference/lambdas.html

 

     高阶函数(Higher-Order Functions)

         将函数作为参数或返回一个函数,称为高阶函数。如“lock()”函数,给对象和函数提供锁功能,获取锁,执行函数,释放锁。

fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}

         该函数的参数“body”是一个函数类型:“() -> T”,表示为一个函数,没有入参,返回一个“T”的值。

        

         通过下面方式调用,需要传入一个函数类型参数:

fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchronized)

         

         另外,也使用使用Lambda表示方式:

val result = lock(lock, { sharedResource.operation() })

 

         Lambda表达式详细内容见“Lambda表达式”部分,这里先简要概述:

         Ø  Lambda表达一般使用“{ }”包围。

         Ø  它的参数(如果有的话)在“->”前定义,参数类型可能是省略的。

         Ø  函数体跟在“->”后面。

 

         在Kotlin中,若函数最后一个参数为函数类型,调用时,该参数可以放到函数“()”的外面:

lock (lock) {
sharedResource.operation()
}

 

         另一个高阶函数例子“map()”:

fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
val result = arrayListOf<R>()
for (item in this)
result.add(transform(item))
return result
}

 

         可以这样调用,当只有Lambda表达式参数时,调用函数时后面的“()”也可以省略:

var ints = asList(1, 2, 3, 4)
val doubledList = ints.map { it -> it * 2 }

 

         若函数参数对应的函数只有一个参数,在使用时,可以省略参数定义,直接使用“it”代替参数:
ints.map { it * 2 }

 

         这种省略参数方式可以写成语言集成查询模式(LINQ-style)代码:
strings.filter { it.length == 5 }.sortBy { it }
.map { it.toUpperCase() }

 

     内联函数(Inline Functions)

         参考内联函数部分。

 

     Lambda表达式和匿名函数

         一个Lambda表达式或一个匿名函数 是 一个函数直接量;即函数本身是没有定义,而是通过立即当做一个函数。如下面的例子:
max(strings, { a, b -> a.length < b.length })

 

         “max”是一个高阶函数,它的第二个参数需要一个函数。第二个参数值本身就是一个函数,即函数直接量;它等同于下面的函数:

fun compare(a: String, b: String): Boolean = a.length < b.length

 

     函数类型(Function Types)

         一个函数接收另外一个函数作为参数,需要指定该参数作为函数类型参数。如“max”函数:

fun <T> max(collection: Collection<T>, 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”的参数,返回一个“Boolean”值的函数;true表示第一个值小于第二个值。

         第4行将“less”当做一个函数使用。

        

         一个函数类型可以通过上面方式实现,若想记录每个参数的意义,也可以定义成一个变量方式:

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

 

       Lambda表达式语法

         Lambda表达式句法形式 就是 一个函数类型文本,如:

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

 

         一个Lambda表达式通常使用“{ }”包围,参数是定义在“()”内,可以添加类型注解,实体部分跟在“->”后面;下面为一个去掉所有的可选注解的Lambda表达:

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


         经常情况下面,Lambda表达式只有一个参数,可以不定义该参数,注解使用“it”关键字代替:

ints.filter { it > 0 } // this literal is of type '(it: Int) -> Boolean'

 

         注:若函数的最后一个参数为函数参数,可以将Lambda表达式定义到参数列表的“()”外面。

 

       匿名函数(Anonymous Functions)

         前面的Lambda表示定义时,可以明确定义返回值类型;在大部分情况下,没有必要明确定义的,因为返回值类型基本都可以自动推断出。

         需要明确定义返回值类型,也可以使用匿名函数(anonymous function)代替。

fun(x: Int, y: Int): Int = x + y

 

         匿名函数除了省略了函数名称,其他跟一般函数的定义基本类似,函数体可以是一个表达式或其一个代码块。

fun(x: Int, y: Int): Int {
return x + y
}

 

         上面的匿名函数的参数及返回类型跟一般函数一样,都是明确定义的;若参数类型可以通过上下文推断出来,也可以省略:

ints.filter(fun(item) = item > 0)

 

         匿名函数的返回类型跟一般函数一样:对应只有一行执行代码的函数,编译器可以自动推断出来返回类型,可以省略;对应多方代码块的函数,需要显示定义返回值类型(为Unit可以省略)。

 

         匿名函数 与 Lambda表示式区别:

         Ø  匿名函数作为参数,一般定义在“()”中;而Lambda表达式可以定义到调用函数“()”外。

         Ø  另外区别在“ 非局部返回( non-local returns ”行为上:非标签注解的return(返回对应的最内层的函数(即fun)),在匿名函数中,退出该匿名函数;而在Lambda表达中,退出包含该表达式的函数。

//在Lambda中使用return
fun testReturn1() {
println("testReturn1-1")

var intList = asList(1, 2, 3, 4)
println(intList)
var reusltList = intList.map {
it * 2
return
}
println(reusltList)
println("testReturn1-2")
}
//输出结果,Lambda中的return返回对对应的被包含的函数(即testReturn1()):
testReturn1-1
[1, 2, 3, 4]

//在匿名函数中使用return
fun testReturn2() {
println("testReturn2-1")

var intList = asList(1, 2, 3, 4)
println(intList)
var reusltList = intList.map(fun(item: Int): Int {
return item * 2
})
println(reusltList)
println("testReturn2-2")
}
//输出结果:
testReturn2-1
[1, 2, 3, 4]
[2, 4, 6, 8]
testReturn2-2

 

       闭包(Closures)

         Lambda表达式及匿名函数(以及 局部函数,对 象表达式)可以访问包含它的外部范围定义的变量(Java中只能是常量,在Kotlin中可以是变量):
var sum = 0ints.filter { it > 0 }.forEach {
sum += it
}
print(sum)

 

       函数文本接收器(Function Literals with Receiver)

         Kotlin提供一种特殊的接收者对象( receiver object),可以访问函数文本。在函数文本的内部,可以访问接收者的成员;类似于扩展函数,在函数体中访问接收者的成员。

        

         对接收者,函数文本相当于一个函数类型:
sum : Int.(other: Int) -> Int//val sum : Int.(other: Int) -> Int = { this + it }

         

         就可以当做一个函数调用:

1.sum(2)

 

         可以使用匿名函数方式:
val sum = fun Int.(other: Int): Int = this + other

 

         可以使用Lambda表达式方式实现:

class HTML {
fun body() { ... }
}

fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // create the receiver object
html.init() // pass the receiver object to the lambda
return html
}

html { // lambda with receiver begins here
body() // calling a method on the receiver object
}