1. Overview
1.概述
In this tutorial, we’ll provide a quick overview of the Functional Java library along with a few examples.
在本教程中,我们将对Functional Java库进行快速概述,并举出几个例子。
2. The Functional Java Library
2.功能化的Java库
The Functional Java library is an open source library meant to facilitate functional programming in Java. The library provides lots of basic and advanced programming abstractions commonly used in Functional Programming.
Functional Java库是一个开源的库,旨在促进Java中的函数式编程。该库提供了很多在函数式编程中常用的基本和高级编程抽象。
Much of the library’s functionality revolves around the F interface. This F interface models a function that takes an input of type A and returns an output of type B. All of this is built on top of Java’s own type system.
该库的大部分功能都围绕着F接口展开。这个F接口为一个函数建模,该函数接收A类型的输入,并返回B类型的输出。所有这些都是建立在Java自己的类型系统之上的。
3. Maven Dependencies
3.Maven的依赖性
First, we need to add the required dependencies to our pom.xml file:
首先,我们需要将所需的依赖性添加到我们的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. Defining a Function
4.定义一个函数
Let’s start by creating a function that we can use in our examples later on.
让我们从创建一个函数开始,我们可以在以后的例子中使用它。
Without Functional Java, a basic multiplication method would look something like:
如果没有Functional Java,一个基本的乘法方法看起来会是这样的。
public static final Integer timesTwoRegular(Integer i) {
return i * 2;
}
Using the Functional Java library, we can define this functionality a little more elegantly:
使用Functional Java库,我们可以更优雅地定义这个功能。
public static final F<Integer, Integer> timesTwo = i -> i * 2;
Above, we see an example of the F interface that takes an Integer as input and returns that Integer times two as its output.
上面,我们看到了一个F接口的例子,它将一个Integer作为输入,并将该Integer乘以2作为其输出。。
Here is another example of a basic function that takes an Integer as input, but in this case, returns a Boolean to indicate if the input was even or odd:
这里是另一个基本函数的例子,它接受一个Integer作为输入,但在这种情况下,返回一个Boolean来表示输入是偶数还是奇数。
public static final F<Integer, Boolean> isEven = i -> i % 2 == 0;
5. Applying a Function
5.应用一个函数
Now that we have our functions in place, let’s apply them to a dataset.
现在我们已经有了我们的函数,让我们把它们应用于一个数据集。
The Functional Java library provides the usual set of types for managing data like lists, sets, arrays, and maps. The key thing to realize is that these data types are immutable.
功能性Java库提供了一系列常用的数据管理类型,如列表、集合、数组和地图。需要认识到的关键一点是,这些数据类型是不可改变的。
Additionally, the library provides convenience functions to convert to and from standard Java Collections classes if needed.
此外,如果需要,该库还提供了方便的函数来转换为标准的Java集合类。
In the example below, we’ll define a list of integers and apply our timesTwo function to it. We’ll also call map using an inline definition of the same function. Of course, we expect the results to be the same:
在下面的例子中,我们将定义一个整数列表并对其应用我们的timesTwo函数。我们还将使用同一函数的内联定义调用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));
}
As we can see map returns a list of the same size where each element’s value is the value of the input list with the function applied. The input list itself does not change.
我们可以看到 map 返回一个相同大小的列表,其中每个元素的值是应用了该函数的输入列表的值。输入列表本身并没有变化。
Here’s a similar example using our isEven function:
下面是一个使用我们的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));
}
Since the map method returns a list, we can apply another function to its output. The order in which we invoke our map functions alters our resulting output:
由于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));
}
The output of the above lists will be:
上述清单的输出将是。
List(3,5,7,9)
List(4,6,8,10)
6. Filtering Using a Function
6.使用函数进行过滤
Another frequently used operation in Functional Programming is to take an input and filter out data based on some criteria. And as you’ve probably already guessed, these filtering criteria are provided in the form of a function. This function will need to return a boolean to indicate whether or not the data needs to be included in the output.
在函数式编程中,另一个经常使用的操作是接受一个输入并根据一些标准过滤掉数据。而你可能已经猜到了,这些过滤标准是以函数的形式提供的。这个函数需要返回一个布尔值,以表明数据是否需要被包含在输出中。
Now, let’s use our isEven function to filter out the odd numbers from an input array using the filter method:
现在,让我们使用我们的isEven函数,用filter方法从一个输入数组中过滤出奇数。
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));
}
One interesting observation is that in this example, we used an Array instead of a List as we used in previous examples, and our function worked fine. Because of the way functions are abstracted and executed, they do not need to be aware of what method was used to collect the input and output.
一个有趣的观察是,在这个例子中,我们使用了一个Array,而不是之前例子中使用的List,我们的函数工作得很好。由于函数被抽象和执行的方式,它们不需要知道是用什么方法来收集输入和输出的。
In this example, we also used our own isEven function, but Functional Java’s own Integer class also has standard functions for basic numerical comparisons.
在这个例子中,我们也使用了自己的isEven函数,但Functional Java自己的Integer类也有标准函数用于基本的数字比较。
7. Applying Boolean Logic Using a Function
7.使用函数应用布尔逻辑
In Functional Programming, we frequently use logic like “only do this if all elements satisfy some condition”, or “only do this if at least one element satisfies some condition”.
在函数式编程中,我们经常使用这样的逻辑:”只有当所有元素都满足某个条件时才这样做”,或者 “只有当至少一个元素满足某个条件时才这样做”。
The Functional Java library provides us with shortcuts for this logic through the exists and the forall methods:
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)));
}
In the example above, we used an array of strings as our input. Calling the fromString function will convert each of the strings from the array into a list of characters. To each of those lists, we applied forall(Characters.isLowerCase).
在上面的例子中,我们用一个字符串数组作为我们的输入。调用fromString函数将把数组中的每个字符串转换成一个字符列表。对这些列表中的每一个,我们应用了forall(Characters.isLowerCase)。
As you probably guessed, Characters.isLowerCase is a function that returns true if a character is lowercase. So applying forall(Characters.isLowerCase) to a list of characters will only return true if the entire list consists of lowercase characters, which in turn then indicates that the original string was all lowercase.
正如你可能猜到的,Characters.isLowerCase是一个函数,如果一个字符是小写的,则返回true。因此,将forall(Characters.isLowerCase)应用于一个字符列表,只有当整个列表由小写字符组成时,才会返回true,这反过来表明原始字符串是全部小写的。
In the first two tests, we used exists because we only wanted to know whether at least one string was lowercase. The third test used forall to verify whether all strings were lowercase.
在前两项测试中,我们使用exists,因为我们只想知道是否至少有一个字符串是小写的。第三个测试使用forall来验证是否所有字符串都是小写字母。
8. Handling Optional Values With a Function
8.用函数处理可选值
Handling optional values in code typically requires == null or isNotBlank checks. Java 8 now provides the Optional class to handle these checks more elegantly, and the Functional Java library offers a similar construct to deal with missing data gracefully through its Option class:
处理代码中的可选值通常需要==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);
}
9. Reducing a Set Using a Function
9.用函数减少一个集合
Finally, we will look at functionality to reduce a set. “Reducing a set” is a fancy way of saying “rolling it up into one value”.
最后,我们将看一下减少一个集合的功能。”减少一个集合 “是 “把它卷成一个值 “的一种华丽的说法。
The Functional Java library refers to this functionality as folding.
Functional Java库将这种功能称为折叠。
A function needs to be specified to indicate what it means to fold the element. An example of this is the Integers.add function to show the integers in an array or list need to be added.
需要指定一个函数来表明折叠元素的含义。这方面的一个例子是Integers.add函数,显示数组或列表中的整数需要被添加。
Based on what the function does when folding, the result can be different depending on whether you start folding from the right or the left. That’s why the Functional Java library provides both versions:
基于函数在折叠时的作用,结果会因你从右边还是左边开始折叠而不同。这就是为什么Functional Java库提供了两个版本。
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);
}
The first foldLeft simply adds all the integers. Whereas the second will first apply a filter and then add the remaining integers.
第一个foldLeft简单地添加所有的整数。而第二个将首先应用一个过滤器,然后添加剩余的整数。
10. Conclusion
10.结论
This article is just a short introduction to the Functional Java library.
这篇文章只是对Functional Java库的一个简短介绍。
As always, the full source code of the article is available over on GitHub.
一如既往,该文章的完整源代码可在GitHub上获得。