注意事项

Java 8包含java.util.function和java.util.stream包,它代替Guava的函数式编程。

Guava的函数式编程可以在Java 8之前的Java版本上使用,也不像Java 8的函数编程需要笨拙而冗长地使用匿名类。

过度使用Guava的函数式编程习惯可能导致代码冗长、混乱、不可读和效率低下。这些都是Guava中最容易也是最常被滥用的部分,当您将代码变成“一行代码”时,Guava团队会哭。

比较如下代码:

Function<String, Integer> lengthFunction = new Function<String, Integer>() {
  public Integer apply(String string) {
    return string.length();
  }
};
Predicate<String> allCaps = new Predicate<String>() {
  public boolean apply(String string) {
    return CharMatcher.javaUpperCase().matchesAllOf(string);
  }
};
Multiset<Integer> lengths = HashMultiset.create(
  Iterables.transform(Iterables.filter(strings, allCaps), lengthFunction));

FluentIterable的版本:

Multiset<Integer> lengths = HashMultiset.create(
        FluentIterable.from(strings)
            .filter(new Predicate<String>() {
                public boolean apply(String string) {
                    return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
                }
            })
            .transform(new Function<String, Integer>() {
                public Integer apply(String string) {
                    return string.length();
                }
            }));
Multiset<Integer> lengths = HashMultiset.create();
    for (String string : strings) {
        if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) {
            lengths.add(string.length());
        }
    }

即使用了静态导入,甚至把Function和Predicate的声明放到别的文件,第一种代码实现仍然不简洁,可读性差并且效率较低。

截至JDK7,命令式代码仍应是默认和第一选择。不应该随便使用函数式风格,除非你绝对确定以下两点之一:

  • 使用函数式风格以后,整个工程的代码行会净减少。在上面的例子中,函数式版本用了11行, 命令式代码用了6行,把函数的定义放到另一个文件或常量中,并不能帮助减少总代码行。
  • 为了提高效率,转换集合的结果需要懒视图,而不是明确计算过的集合。此外,确保你已经阅读和重读了Effective Java的第55条,并且除了阅读本章后面的说明,你还真正做了性能测试并且有测试数据来证明函数式版本更快。

请务必确保,当使用Guava函数式的时候,用传统的命令式做同样的事情不会更具可读性。尝试把代码写下来,看看它是不是真的那么糟糕?会不会比你想尝试的极其笨拙的函数式 更具可读性。

Functions[函数]和Predicates[断言]

本节只讨论直接与Function和Predicate打交道的Guava功能。一些其他工具类也和”函数式风格”相关,例如 Iterables.concat(Iterable),和其他用常量时间返回视图的方法。尝试看看集合工具类。

Guava提供两个基本的函数式接口:

  • Function<A, B>,它声明了单个方法B apply(A input)。Function对象通常被预期为引用透明的——没有副作用——并且引用透明性中的”相等”语义与equals一致,如a.equals(b)意味着function.apply(a).equals(function.apply(b))。
  • Predicate,它声明了单个方法 boolean apply(T input)。Predicate 对象通常也被预期为无副作用函数,并且”相等”语义与equals一致。

特殊的Predicates

字符类型有自己特定版本的Predicate——CharMatcher,它通常更高效,并且在某些需求方面更有用。CharMatcher实现了Predicate,可以当作 Predicate一样使用,要把Predicate转成CharMatcher,可以使用CharMatcher.forPredicate。更多细节请参考字符串处理。

此外,对可比较类型和基于比较逻辑的Predicate,Range类可以满足大多数需求——它表示一个不可变区间。Range类实现了Predicate,用以判断值是否在区间内。例如,Range.atMost(2)就是个完全合法的 Predicate。更多使用Range的细节。

操作Functions和Predicates

Functions, 提供了简单的函数构造和使用方法,包括:

Predicates中可用的构造和使用方法要多得多,示例包括:

有关详细信息,请参考Javadoc。

使用函数式编程

Guava提供了许多使用函数式编程操作集合的工具。这些通常可以在集合实用程序类Iterables, Lists, Sets, Maps, Multimaps等找到。

Predicates最基本的用途是过滤集合。所有Guava过滤器方法都返回视图。

【腾讯云】境外1核2G服务器低至2折,半价续费券限量免费领取!
https://cloud.tencent.com/act/cps/redirect?redirect=1068&cps_key=e4b50f6c64a4480367f8a8d16fd07c5a&from=console

标签: Guava, Function, FluentIterable, Predicates

添加新评论