1. 概述
本篇文章将带你快速了解 Functional Java 这个库,并通过几个实际例子展示它的基本用法。
2. Functional Java 库简介
Functional Java 是一个开源库,旨在帮助 Java 开发者更方便地进行函数式编程。它提供了一系列基础和高级的编程抽象,这些抽象在函数式编程中非常常见。
这个库的核心围绕着 F
接口展开。✅ 这个接口表示一个函数:接收类型为 A
的输入,返回类型为 B
的输出。 所有功能都构建在 Java 自身的类型系统之上。
3. Maven 依赖配置
首先,我们需要在 pom.xml
文件中添加所需的依赖:
<dependency>
<groupId>org.functionaljava</groupId>
<artifactId>functionaljava</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>org.functionaljava</groupId>
<artifactId>functionaljava-java8</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>org.functionaljava</groupId>
<artifactId>functionaljava-quickcheck</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>org.functionaljava</groupId>
<artifactId>functionaljava-java-core</artifactId>
<version>4.8.1</version>
</dependency>
4. 定义函数
我们先来定义一个简单的函数,供后续示例使用。
不使用 Functional Java 时,一个乘二的方法可能是这样的:
public static final Integer timesTwoRegular(Integer i) {
return i * 2;
}
而使用 Functional Java 后,我们可以更优雅地定义:
public static final F<Integer, Integer> timesTwo = i -> i * 2;
✅ 上面这段代码定义了一个 F
接口的实例,它接受一个 Integer
类型的输入,并返回该值的两倍。
再来看一个类似的函数,这次判断一个整数是否为偶数:
public static final F<Integer, Boolean> isEven = i -> i % 2 == 0;
5. 应用函数
有了函数之后,我们可以将其应用于数据集合。
Functional Java 提供了常见的数据类型,如 List、Set、Array 和 Map。⚠️ 需要注意的是,这些数据类型都是不可变的(immutable)。
此外,库中还提供了便捷方法,用于与 Java 标准集合类之间的转换。
下面是一个例子,我们将一个整数列表中的每个元素都乘以 2,并通过 map
方法实现:
public void multiplyNumbers_givenIntList_returnTrue() {
List<Integer> fList = List.list(1, 2, 3, 4);
List<Integer> fList1 = fList.map(timesTwo);
List<Integer> fList2 = fList.map(i -> i * 2);
assertTrue(fList1.equals(fList2));
}
✅ map
会返回一个同样大小的列表,其中每个元素是原列表元素经过函数处理后的结果,原始列表本身不会改变。
下面是使用 isEven
函数的例子:
public void calculateEvenNumbers_givenIntList_returnTrue() {
List<Integer> fList = List.list(3, 4, 5, 6);
List<Boolean> evenList = fList.map(isEven);
List<Boolean> evenListTrueResult = List.list(false, true, false, true);
assertTrue(evenList.equals(evenListTrueResult));
}
✅ 由于 map
返回的是一个新的列表,我们可以继续对其结果再次调用 map
。⚠️ 调用顺序会影响最终结果:
public void applyMultipleFunctions_givenIntList_returnFalse() {
List<Integer> fList = List.list(1, 2, 3, 4);
List<Integer> fList1 = fList.map(timesTwo).map(plusOne);
List<Integer> fList2 = fList.map(plusOne).map(timesTwo);
assertFalse(fList1.equals(fList2));
}
输出结果分别为:
List(3,5,7,9)
List(4,6,8,10)
6. 使用函数进行过滤
在函数式编程中,另一个常见操作是根据某些条件过滤数据。这些条件通常也以函数的形式提供,且必须返回布尔值。
下面的例子中,我们使用 isEven
函数过滤掉奇数:
public void filterList_givenIntList_returnResult() {
Array<Integer> array = Array.array(3, 4, 5, 6);
Array<Integer> filteredArray = array.filter(isEven);
Array<Integer> result = Array.array(4, 6);
assertTrue(filteredArray.equals(result));
}
⚠️ 这次我们使用的是 Array
而不是 List
,但函数依然适用。✅ 这体现了函数抽象的优势:函数无需关心数据结构的具体类型。
Functional Java 的 Integer
类也提供了类似的标准函数,比如用于数值比较的函数。
7. 使用函数进行布尔逻辑判断
函数式编程中经常需要判断“是否所有元素满足某个条件”或“是否存在至少一个元素满足某个条件”。
Functional Java 提供了 exists
和 forall
两个方法来简化这类逻辑:
public void checkForLowerCase_givenStringArray_returnResult() {
Array<String> array = Array.array("Welcome", "To", "baeldung");
assertTrue(array.exists(s -> List.fromString(s).forall(Characters.isLowerCase)));
Array<String> array2 = Array.array("Welcome", "To", "Baeldung");
assertFalse(array2.exists(s -> List.fromString(s).forall(Characters.isLowerCase)));
assertFalse(array.forall(s -> List.fromString(s).forall(Characters.isLowerCase)));
}
在这个例子中,我们将字符串数组中的每个字符串转为字符列表,并对每个字符列表应用 forall(Characters.isLowerCase)
。
✅ Characters.isLowerCase
是一个判断字符是否为小写的函数。因此,只有当整个字符串都为小写时,forall
才会返回 true
。
前两个测试使用了 exists
来判断是否存在至少一个小写字符串,第三个测试使用 forall
来验证是否所有字符串都为小写。
8. 使用函数处理可选值
在 Java 中处理可选值通常需要 null
检查或 isNotBlank
判断。Java 8 引入了 Optional
类型来优雅地处理这种情况。Functional Java 也提供了类似的机制:Option
类。
public void checkOptions_givenOptions_returnResult() {
Option<Integer> n1 = Option.some(1);
Option<Integer> n2 = Option.some(2);
Option<Integer> n3 = Option.none();
F<Integer, Option<Integer>> function = i -> i % 2 == 0 ? Option.some(i + 100) : Option.none();
Option<Integer> result1 = n1.bind(function);
Option<Integer> result2 = n2.bind(function);
Option<Integer> result3 = n3.bind(function);
assertEquals(Option.none(), result1);
assertEquals(Option.some(102), result2);
assertEquals(Option.none(), result3);
}
✅ bind
方法用于将函数应用于 Option
值,如果值存在则执行函数,否则返回 none
。
9. 使用函数归约集合
“归约”操作(Reduce)指的是将集合中的多个元素合并为一个值。
Functional Java 将这一操作称为 折叠(fold)。
需要指定一个函数来定义如何“折叠”元素。比如使用 Integers.add
函数来对整数列表求和。
根据折叠方向的不同,结果可能会不同。Functional Java 提供了 foldLeft
和 foldRight
两种方式:
public void foldLeft_givenArray_returnResult() {
Array<Integer> intArray = Array.array(17, 44, 67, 2, 22, 80, 1, 27);
int sumAll = intArray.foldLeft(Integers.add, 0);
assertEquals(260, sumAll);
int sumEven = intArray.filter(isEven).foldLeft(Integers.add, 0);
assertEquals(148, sumEven);
}
第一个 foldLeft
对所有元素求和;第二个先过滤出偶数再求和。
10. 小结
本文只是对 Functional Java 库的一个简单介绍。✅ 如果你对函数式编程感兴趣,这个库值得深入研究。
源码可从 GitHub 获取。