From b2c4b990b71b61be377f9dca25987d3ce388e02e Mon Sep 17 00:00:00 2001 From: xiangflight Date: Thu, 15 Aug 2019 22:31:36 +0800 Subject: [PATCH] =?UTF-8?q?revision[13]=20=E4=BD=9C=E4=B8=BA=E9=97=AD?= =?UTF-8?q?=E5=8C=85=E7=9A=84=E5=86=85=E9=83=A8=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/13-Functional-Programming.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/book/13-Functional-Programming.md b/docs/book/13-Functional-Programming.md index c3a612b..95d622d 100644 --- a/docs/book/13-Functional-Programming.md +++ b/docs/book/13-Functional-Programming.md @@ -1048,7 +1048,7 @@ O **闭包**(Closure)一词总结了这些问题。 它非常重要,利用闭包可以轻松生成函数。 -考虑一个更复杂的 Lambda,它使用函数作用域之外的变量。 返回该函数会发生什么? 也就是说,当你调用函数时,它对那些 “外部 ”变量引用了什么? 如果语言不能自动解决这个问题,那将变得非常具有挑战性。 能够解决这个问题的语言被称为**支持闭包**,或者叫作在词法上限定范围( 也使用术语变量捕获 )。Java 8 提供了有限但合理的闭包支持,我们将用一些简单的例子来研究它。 +考虑一个更复杂的 Lambda,它使用函数作用域之外的变量。 返回该函数会发生什么? 也就是说,当你调用函数时,它对那些 “外部 ”变量引用了什么? 如果语言不能自动解决这个问题,那将变得非常具有挑战性。 能够解决这个问题的语言被称为**支持闭包**,或者叫作在词法上限定范围( 也使用术语*变量捕获* )。Java 8 提供了有限但合理的闭包支持,我们将用一些简单的例子来研究它。 首先,下例函数中,方法返回访问对象字段和方法参数。代码示例: @@ -1094,7 +1094,7 @@ public class SharedStorage { 每次调用 `getAsInt()` 都会增加 `i`,表明存储是共享的。 -如果 `i` 是 `makeFun()` 的本地怎么办? 在正常情况下,当 `makeFun()` 完成时 `i` 就消失。 但它仍然编译: +如果 `i` 是 `makeFun()` 的局部变量怎么办? 在正常情况下,当 `makeFun()` 完成时 `i` 就消失。 但它仍可以编译: ```java // functional/Closure2.java @@ -1126,7 +1126,7 @@ public class Closure3 { } ``` -`x` 和 `i` 的操作都犯了同样的错误:从 Lambda 表达式引用的局部变量必须是 `final` 或者实 `final` 效果的。 +`x` 和 `i` 的操作都犯了同样的错误:从 Lambda 表达式引用的局部变量必须是 `final` 或者是等同 `final` 效果的。 如果使用 `final` 修饰 `x`和 `i`,就不能再递增它们的值了。代码示例: @@ -1165,7 +1165,7 @@ public class Closure5 { } ``` -我们可以通过将 `final` 关键字应用于变量声明来实现**等同 final 效果**, 不用更改任何其余代码。 实际上它就是具备 `final` 效果的,只是没有明确说明。 +**等同 final 效果**意味着可以在变量声明前加上 **final** 关键字而不用更改任何其余代码。 实际上它就是具备 `final` 效果的,只是没有明确说明。 通过在闭包中使用 `final` 关键字提前修饰变量 `x` 和 `i` , 我们解决了 `Closure5.java` 中的问题。代码示例: @@ -1206,7 +1206,7 @@ public class Closure7 { } ``` -编译器非常智能,它能识别变量 `i` 的值正在被更改。 对于包装类型的处理可能比较特殊,所以让我们使用下面的 List 的例子。代码示例: +编译器非常智能,它能识别变量 `i` 的值被更改过了。 对于包装类型的处理可能比较特殊,因此我们尝试下 **List**: ```java // functional/Closure8.java @@ -1244,7 +1244,7 @@ public class Closure8 { [1, 96] ``` -可以看到,这次一切正常。我们改变了 List 的值却没产生编译时错误。通过观察本例的输出结果,我们发现这看起来非常安全。这是因为每次调用 `makeFun()` 时,其实都会创建并返回一个全新的 `ArrayList`。 也就是说,每个闭包都有自己独立的 `ArrayList` 他们不能互相干扰和共享。 +可以看到,这次一切正常。我们改变了 **List** 的值却没产生编译时错误。通过观察本例的输出结果,我们发现这看起来非常安全。这是因为每次调用 `makeFun()` 时,其实都会创建并返回一个全新的 `ArrayList`。 也就是说,每个闭包都有自己独立的 `ArrayList`, 它们之间互不干扰。 请**注意**我已经声明 `ai` 是 `final` 的了。尽管在这个例子中你可以去掉 `final` 并得到相同的结果(试试吧!)。 应用于对象引用的 `final` 关键字仅表示不会重新赋值引用。 它并不代表你不能修改对象本身。 @@ -1271,6 +1271,7 @@ public class Closure9 { 让我们回顾一下 `Closure1.java`。那么现在问题来了:为什么变量 `i` 被修改编译器却没有报错呢。 它既不是 `final` 的,也不是**等同 final 效果**的。因为 `i` 是外围类的成员,所以这样做肯定是安全的(除非你正在创建共享可变内存的多个函数)。是的,你可以辩称在这种情况下不会发生变量捕获(Variable Capture)。但可以肯定的是,`Closure3.java` 的错误消息是专门针对局部变量的。因此,规则并非只是“在 Lambda 之外定义的任何变量必须是 `final` 的或**等同 final 效果**那么简单。相反,你必须考虑捕获的变量是否是**等同 final 效果**的。 如果它是对象中的字段,那么它拥有独立的生存周期,并且不需要任何特殊的捕获,以便稍后在调用 Lambda 时存在。 + ### 作为闭包的内部类 我们可以复制我们的例子使用匿名内部类: