mirror of
https://github.com/LingCoder/OnJava8.git
synced 2026-08-24 06:53:27 +08:00
@@ -1274,7 +1274,7 @@ public class Closure9 {
|
||||
|
||||
### 作为闭包的内部类
|
||||
|
||||
我们可以复制我们的例子使用匿名内部类:
|
||||
我们可以使用匿名内部类重写之前的例子:
|
||||
|
||||
```java
|
||||
// functional/AnonymousClosure.java
|
||||
@@ -1300,7 +1300,7 @@ public class AnonymousClosure {
|
||||
## 函数组合
|
||||
|
||||
|
||||
函数组合(Function Composition)意为“多个函数组合成新函数”。它通常是函数式编程的基本组成部分。在前面的 `TransformFunction.java` 类中,有一个使用 `andThen()` 的函数组合示例。在 `java.util.function` 包里包含支持函数组合的一些接口方法 [^7]。
|
||||
函数组合(Function Composition)意为“多个函数组合成新函数”。它通常是函数式编程的基本组成部分。在前面的 `TransformFunction.java` 类中,有一个使用 `andThen()` 的函数组合示例。一些 `java.util.function` 接口中包含支持函数组合的方法 [^7]。
|
||||
|
||||
| 组合方法 | 支持接口 |
|
||||
| :----- | :----- |
|
||||
@@ -1343,7 +1343,7 @@ _fter _ll _mbul_nces
|
||||
|
||||
这里我们重点看正在创建的新函数 `f4`。它调用 `apply()` 的方式与常规几乎无异[^8]。
|
||||
|
||||
当 `f1` 获得字符串时,它已经被`f2` 剥离了前三个字符。这是因为对 `compose(f2)`的调用意味着在 `f1` 之前调用 `f2`。
|
||||
当 `f1` 获得字符串时,它已经被`f2` 剥离了前三个字符。这是因为 `compose(f2)` 表示 `f2` 的调用发生在 `f1` 之前。
|
||||
|
||||
下例是 `Predicate` 的逻辑运算演示.代码示例:
|
||||
|
||||
@@ -1376,14 +1376,13 @@ foobaz
|
||||
|
||||
`p4` 获取到了所有断言并组合成一个更复杂的断言。解读:如果字符串中不包含 `bar` 且长度小于 5,或者它包含 `foo` ,则结果为 `true`。
|
||||
|
||||
正因它产生如此清晰的语法,我在主方法中采用了一些小技巧,并借用了下一章的内容。首先,我创建一个流的字符串对象集合,然后将每个对象传递给 `filter()` 操作。 `filter()` 使用 `p4` 的断言来确定对象的去留。最后我们使用 `forEach()` 和 `println` 方法打印出留下来的对象。
|
||||
正因它产生如此清晰的语法,我在主方法中采用了一些小技巧,并借用了下一章的内容。首先,我创建了一个字符串对象的流,然后将每个对象传递给 `filter()` 操作。 `filter()` 使用 `p4` 的断言来确定对象的去留。最后我们使用 `forEach()` 将 `println` 方法引用应用在每个留存的对象上。
|
||||
|
||||
从输出结果我们可以看到 `p4` 的工作流程:任何带有 `foo` 的东西都会留下,即使它的长度大于 5。 `fongopuckey` 因长度超出和不包含 `bar` 而被丢弃。
|
||||
|
||||
<!-- Currying and Partial Evaluation -->
|
||||
## 柯里化和部分求值
|
||||
|
||||
|
||||
[柯里化](https://en.wikipedia.org/wiki/Currying)(Currying)的名称来自于其发明者之一 *Haskell Curry*。他可能是计算机领域唯一名字被命名重要概念的人(另外就是 Haskell 编程语言)。 柯里化意为:将一个多参数的函数,转换为一系列单参数函数。
|
||||
|
||||
```java
|
||||
@@ -1458,7 +1457,7 @@ public class Curry3Args {
|
||||
Hi Ho Hup
|
||||
```
|
||||
|
||||
对于每个级别的箭头级联(Arrow-cascading),你可以围绕类型声明包装成另一个函数。
|
||||
对于每个级别的箭头级联(Arrow-cascading),你在类型声明中包裹了另一个 **Function**。
|
||||
|
||||
处理基本类型和装箱时,请使用适当的 **Function** 接口:
|
||||
|
||||
@@ -1473,7 +1472,7 @@ public class CurriedIntAdd {
|
||||
curriedIntAdd = a -> b -> a + b;
|
||||
IntUnaryOperator add4 = curriedIntAdd.apply(4);
|
||||
System.out.println(add4.applyAsInt(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1483,13 +1482,13 @@ public class CurriedIntAdd {
|
||||
9
|
||||
```
|
||||
|
||||
更多柯里化示例,可搜索互联网。通常这些是 Java 以外的语言,理解它们的基本概念,就应该很容易翻译。
|
||||
可以在互联网上找到更多的柯里化示例。通常它们是用 Java 之外的语言实现的,但如果理解了柯里化的基本概念,你可以很轻松地用 Java 实现它们。
|
||||
|
||||
<!-- Pure Functional Programming -->
|
||||
## 纯函数式编程
|
||||
|
||||
|
||||
即使没有函数式支持,像 C 这样的基础语言,也可以按照一定的原则编写纯函数程序。Java 8 让函数式编程更简单,不过我们要确保一切是 `final` 的,同时你的所有方法和函数没有副作用。因为 Java 在本质上并非是不可变语言,我们无法通过编译器查错。
|
||||
即使没有函数式支持,像 C 这样的基础语言,也可以按照一定的原则编写纯函数式程序。Java 8 让函数式编程更简单,不过我们要确保一切是 `final` 的,同时你的所有方法和函数没有副作用。因为 Java 在本质上并非是不可变语言,我们无法通过编译器查错。
|
||||
|
||||
这种情况下,我们可以借助第三方工具[^9],但使用 Scala 或 Clojure 这样的语言可能更简单。因为它们从一开始就是为保持不变性而设计的。你可以采用这些语言来编写你的 Java 项目的一部分。如果必须要用纯函数式编写,则可以用 Scala(需要一些规则) 或 Clojure (需要的规则更少)。虽然 Java 支持[并发编程](./24-Concurrent-Programming.md),但如果这是你项目的核心部分,你应该考虑在项目部分功能中使用 `Scala` 或 `Clojure` 之类的语言。
|
||||
|
||||
@@ -1509,7 +1508,7 @@ Lambda 表达式和方法引用并没有将 Java 转换成函数式语言,而
|
||||
|
||||
[^1]: 功能粘贴在一起的方法的确有点与众不同,但它仍不失为一个库。
|
||||
[^2]: 例如,这个电子书是利用 [Pandoc](http://pandoc.org/) 制作出来的,它是用纯函数式语言 [Haskell](https://www.haskell.org/) 编写的一个程序 。
|
||||
[^3]: 有时函数式语言将其描述为“代码即数据”。“
|
||||
[^3]: 有时函数式语言将其描述为“代码即数据”。
|
||||
[^4]: 这个语法来自 C++。
|
||||
[^5]: 我还没有验证过这种说法。
|
||||
[^6]: 当你理解了[并发编程](./24-Concurrent-Programming.md)章节的内容,你就能明白为什么更改共享变量 “不是线程安全的” 的了。
|
||||
|
||||
Reference in New Issue
Block a user