参考原文: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
}