首页 > 编程笔记 > Java笔记 阅读:14

Java中的高阶函数(附带实例)

在函数式编程语言中,函数是“一等公民”。就像可以将数值传递给方法,并且可以使用生成数值的方法一样,你也可以将函数作为参数和返回值。

处理或返回函数的函数称为高阶函数(high-order function)。这听起来很抽象,但在实践中非常有用。

Java 不完全是一种函数式语言,即使使用了函数式接口,但原理是相同的。接下来我们将查看一些示例,并探讨 Comparator 接口中的高阶函数。

Java返回函数的方法

假设有时我们希望按升序对字符串数组进行排序,有时又希望按降序排序。可以制作一个产生正确比较器的方法:
public static Comparator<String> compareInDirecton(int direction) {
    return (x, y) -> direction * x.compareTo(y);
}
调用 compareInDirection(1) 将生成一个升序比较器,调用 compareInDirection(-1) 将生成一种降序比较器。

结果可以传递给另一个方法(如 Arrays.sort),该方法期望使用以下接口:
Arrays.sort(names, compareInDirection(-1));
一般来说,不要羞于编写生成函数的方法(或者从技术上讲,是实现函数式接口的类的实例)。这对于生成自定义函数并将其传递给具有函数式接口的方法的非常有用。

Java修改函数的方法

在前文中,你看到了一个生成递增或递减字符串比较器的方法。我们可以通过反转任何比较器来推广这一观点:
public static Comparator<String> reverse(Comparator<String> comp) {
    return (x, y) -> comp.compare(y, x);
}
此方法对函数进行操作。它接收函数并返回修改后的函数。要获得不区分大小写的降序,请使用:
reverse(String::compareToIgnoreCase)
注意,Comparator 接口有一个默认方法 reversed,以这种方式生成给定比较器的反向比较形式。

Java Comparator方法

Comparator 接口有许多有用的静态方法,这些方法是生成比较器的高阶函数。

comparing 方法接受一个“键提取器”函数,将类型 T 映射为一个可比较类型(如 String)。该函数应用于要比较的对象,然后对返回的键进行比较。例如,假设 Person 类有一个方法 getLastName。然后,可以按姓氏对 Person 对象数组进行排序,如下所示:
Arrays.sort(people, Comparator.comparing(Person::getLastName));

可以使用 thenComparing 方法链接比较器,来处理比较结果相同的情况。例如,按姓氏对一组人进行排序,然后对姓氏相同的人使用名字进行排序:
Arrays.sort(people, Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName));
这些方法有很多变体形式。可以指定一个比较器,用于 comparing 和 thenComparing 方法提取的键。

例如,这里我们根据人名长度对人进行排序:
Arrays.sort(people, Comparator.comparing(Person::getLastName,(s, t) -> s.length() - t.length()));

此外,comparing 和 thenComparing 方法都有避免 int、long 或 double 值自动装箱的变体。按人名长度排序的更简单的方法是:
Arrays.sort(people, Comparator.comparingInt(p -> p.getLastName().length()));
如果键函数可能返回 null,那么可能需要用到 nullsFirst 和 nullsLast 适配器。这些静态方法采用现有的比较器并对其进行修改,从而在遇到空值时不会引发异常,而是将这个值标记为小于或大于正常值。

例如,假设当一个人没有中间名时,getMiddleName 返回 null,你就可以使用:
Comparator.comparing(Person::getMiddleName(), Comparator.nullsFirst(...))
在这种情况下,nullsFirst 方法需要一个比较器,它可以比较两个字符串。

naturalOrder 方法可以为实现了 Comparable 接口的任何类生成一个比较器。这里是按中间名可能为空进行排序的完整调用。使用静态导入 java.util.Comparator.*,以使表达式更清晰。注意,naturalOrder 的类型是推导得到的:
Arrays.sort(people, comparing(Person::getMiddleName, nullsFirst(naturalOrder())));
静态 reverseOrder 方法会提供自然顺序的逆序。

相关文章