1048
1048
1049
1049
** 闭包** (Closure)一词总结了这些问题。 它非常重要,利用闭包可以轻松生成函数。
1050
1050
1051
- 考虑一个更复杂的 Lambda,它使用函数作用域之外的变量。 返回该函数会发生什么? 也就是说,当你调用函数时,它对那些 “外部 ”变量引用了什么? 如果语言不能自动解决这个问题,那将变得非常具有挑战性。 能够解决这个问题的语言被称为** 支持闭包** ,或者叫作在词法上限定范围( 也使用术语变量捕获 )。Java 8 提供了有限但合理的闭包支持,我们将用一些简单的例子来研究它。
1051
+ 考虑一个更复杂的 Lambda,它使用函数作用域之外的变量。 返回该函数会发生什么? 也就是说,当你调用函数时,它对那些 “外部 ”变量引用了什么? 如果语言不能自动解决这个问题,那将变得非常具有挑战性。 能够解决这个问题的语言被称为** 支持闭包** ,或者叫作在词法上限定范围( 也使用术语 * 变量捕获 * )。Java 8 提供了有限但合理的闭包支持,我们将用一些简单的例子来研究它。
1052
1052
1053
1053
首先,下例函数中,方法返回访问对象字段和方法参数。代码示例:
1054
1054
@@ -1094,7 +1094,7 @@ public class SharedStorage {
1094
1094
1095
1095
每次调用 ` getAsInt() ` 都会增加 ` i ` ,表明存储是共享的。
1096
1096
1097
- 如果 ` i ` 是 ` makeFun() ` 的本地怎么办 ? 在正常情况下,当 ` makeFun() ` 完成时 ` i ` 就消失。 但它仍然编译 :
1097
+ 如果 ` i ` 是 ` makeFun() ` 的局部变量怎么办 ? 在正常情况下,当 ` makeFun() ` 完成时 ` i ` 就消失。 但它仍可以编译 :
1098
1098
1099
1099
``` java
1100
1100
// functional/Closure2.java
@@ -1126,7 +1126,7 @@ public class Closure3 {
1126
1126
}
1127
1127
```
1128
1128
1129
- ` x ` 和 ` i ` 的操作都犯了同样的错误:从 Lambda 表达式引用的局部变量必须是 ` final ` 或者实 ` final ` 效果的。
1129
+ ` x ` 和 ` i ` 的操作都犯了同样的错误:从 Lambda 表达式引用的局部变量必须是 ` final ` 或者是等同 ` final ` 效果的。
1130
1130
1131
1131
如果使用 ` final ` 修饰 ` x ` 和 ` i ` ,就不能再递增它们的值了。代码示例:
1132
1132
@@ -1165,7 +1165,7 @@ public class Closure5 {
1165
1165
}
1166
1166
```
1167
1167
1168
- 我们可以通过将 ` final ` 关键字应用于变量声明来实现 ** 等同 final 效果** , 不用更改任何其余代码 。 实际上它就是具备 ` final ` 效果的,只是没有明确说明。
1168
+ ** 等同 final 效果** 意味着可以在变量声明前加上 ** final ** 关键字而不用更改任何其余代码 。 实际上它就是具备 ` final ` 效果的,只是没有明确说明。
1169
1169
1170
1170
通过在闭包中使用 ` final ` 关键字提前修饰变量 ` x ` 和 ` i ` , 我们解决了 ` Closure5.java ` 中的问题。代码示例:
1171
1171
@@ -1206,7 +1206,7 @@ public class Closure7 {
1206
1206
}
1207
1207
```
1208
1208
1209
- 编译器非常智能,它能识别变量 ` i ` 的值正在被更改 。 对于包装类型的处理可能比较特殊,所以让我们使用下面的 List 的例子。代码示例 :
1209
+ 编译器非常智能,它能识别变量 ` i ` 的值被更改过了 。 对于包装类型的处理可能比较特殊,因此我们尝试下 ** List** :
1210
1210
1211
1211
``` java
1212
1212
// functional/Closure8.java
@@ -1244,7 +1244,7 @@ public class Closure8 {
1244
1244
[1, 96]
1245
1245
```
1246
1246
1247
- 可以看到,这次一切正常。我们改变了 List 的值却没产生编译时错误。通过观察本例的输出结果,我们发现这看起来非常安全。这是因为每次调用 ` makeFun() ` 时,其实都会创建并返回一个全新的 ` ArrayList ` 。 也就是说,每个闭包都有自己独立的 ` ArrayList ` 他们不能互相干扰和共享 。
1247
+ 可以看到,这次一切正常。我们改变了 ** List** 的值却没产生编译时错误。通过观察本例的输出结果,我们发现这看起来非常安全。这是因为每次调用 ` makeFun() ` 时,其实都会创建并返回一个全新的 ` ArrayList ` 。 也就是说,每个闭包都有自己独立的 ` ArrayList ` , 它们之间互不干扰 。
1248
1248
1249
1249
请** 注意** 我已经声明 ` ai ` 是 ` final ` 的了。尽管在这个例子中你可以去掉 ` final ` 并得到相同的结果(试试吧!)。 应用于对象引用的 ` final ` 关键字仅表示不会重新赋值引用。 它并不代表你不能修改对象本身。
1250
1250
@@ -1271,6 +1271,7 @@ public class Closure9 {
1271
1271
让我们回顾一下 ` Closure1.java ` 。那么现在问题来了:为什么变量 ` i ` 被修改编译器却没有报错呢。 它既不是 ` final ` 的,也不是** 等同 final 效果** 的。因为 ` i ` 是外围类的成员,所以这样做肯定是安全的(除非你正在创建共享可变内存的多个函数)。是的,你可以辩称在这种情况下不会发生变量捕获(Variable Capture)。但可以肯定的是,` Closure3.java ` 的错误消息是专门针对局部变量的。因此,规则并非只是“在 Lambda 之外定义的任何变量必须是 ` final ` 的或** 等同 final 效果** 那么简单。相反,你必须考虑捕获的变量是否是** 等同 final 效果** 的。 如果它是对象中的字段,那么它拥有独立的生存周期,并且不需要任何特殊的捕获,以便稍后在调用 Lambda 时存在。
1272
1272
1273
1273
<!-- Inner Classes as Closures -->
1274
+
1274
1275
### 作为闭包的内部类
1275
1276
1276
1277
我们可以复制我们的例子使用匿名内部类:
0 commit comments