Kotlin函数式编程 (3)✔️闭包与捕获变量
- 闭包定义
- Java 与 Koltin 中 Lambda 捕获局部变量区别
- 闭包捕获的变量可以脱离原始作用域而存在
一、闭包定义
闭包 是一种特殊的函数,它可以访问函数体之外的变量,这个变量和函数一同存在,即使已经离开了它的原始作用域也不例外。这种特殊函数一般是局部函数、匿名函数或 Lambda 表达式。
闭包可以访问函数体之外的变量,这个过程称为捕获变量。
// 全局变量
var value = 0
fun main(args: Array<String>?) {
// 局部变量
var localValue = 20
val result = { a: Int ->
value++
localValue++
val c = a + value + localValue
println(c)
}
result(30)
println("value = $value")
println("localValue = $localValue")
}
2019-06-05 16:53:27.124 28646-28646/cn.ak.kot I/System.out: 52
2019-06-05 16:53:27.124 28646-28646/cn.ak.kot I/System.out: value = 1
2019-06-05 16:53:27.125 28646-28646/cn.ak.kot I/System.out: localValue = 21
上述代码中 闭包是捕获 value 和 localValue 变量的 Lambda 表达式。
二、Java 与 Koltin 中 Lambda 捕获局部变量区别
Java 中 Lambda 表达式捕获局部变量时,局部变量只能是 final 的。在 Lambda 体中只能读取局部变量,不能修改局部变量。而 kotlin 中没有这个限制,可以读取和修改局部变量。如下面代码:
// 声明了一个Java代码接口
@FunctionalInterface
public interface Clickable {
void onClick();
}
// Java中的Lambda表达式局部变量捕获
public class Closure {
private void closure(Clickable clickable) {
clickable.onClick();
}
public void main(ArrayList<String> args) {
int count = 0;
closure(() -> {
count += 1; // 1️⃣编译错误,count需要使用final修饰
});
System.out.println(count);
}
}
Java中代码第1️⃣行是编译不过的,必须设置为 count 为 final 才能通过编译,但又不能对 count 进行修改,如果非要修改 count 只能把 count 声明为 Closure 的成员变量。
对比 Kotlin 代码实现:
class Closure {
private fun closure(clickable: Clickable) {
clickable.onClick()
}
fun main(args: Array<String>) {
var count: Int = 0
closure(Clickable { count += 1 }) // 编译正常
println(count) // 2
}
}
三、闭包捕获的变量可以脱离原始作用域而存在
闭包捕获变量后,这些变量被保存在一个特殊的容器中村存储起来。即便是声明这些变量的原始作用域已经不存在,闭包中仍然可以访问这些变量。
fun sum(): (Int) -> Int { // 2️⃣
var total = 0 // 3️⃣
return { // 4️⃣
total += it
total
}
}
fun main(args: Array<String>?) {
val f1 = sum() // 5️⃣
println(f1(10))
println(f1(10))
println(f1(10))
println(f1(10))
}
2019-06-05 17:20:55.912 30314-30314/cn.ak.kot I/System.out: 10
2019-06-05 17:20:55.912 30314-30314/cn.ak.kot I/System.out: 20
2019-06-05 17:20:55.912 30314-30314/cn.ak.kot I/System.out: 30
2019-06-05 17:20:55.913 30314-30314/cn.ak.kot I/System.out: 40
代码第1️⃣行声明高阶函数sum()
返回值类型为(Int) -> Int
函数类型;第3️⃣行定义了一个total
局部变量,作用域为sum()
高阶函数内;第4️⃣行使用 Lambda 表达式作为 高阶函数sum()
返回值。
代码第5️⃣行声明变量 f1,注意 f1 类型是(Int) -> Int
的 Lambda表达式。连续执行 4次f1(10)
后打印结果为10、20、30、40
的累加结果,f1 又是Lambda表达式引用,而Lambda表达式的返回值是局部变量total
,但是total
的作用域是在高阶函数sum()
中,每次 f1 执行完时,total
变量的作用域就不存在了,可是 total
变量值都能够被保持,由此说明被捕获的变量都存储在一个特殊的容器内,而这个变量和函数一同存在。