本章主要介绍Guava提供的函数式编程工具类。

警告!!

在jdk8以前,函数式编程都只能通过丑陋冗长尴尬的匿名内部类来实现。 所以你们懂得,即使是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.JAVA_UPPER_CASE.matchesAllOf(string);
  }
};
Multiset<Integer> lengths = HashMultiset.create(
  Iterables.transform(Iterables.filter(strings, allCaps), lengthFunction));

还有下面这个:

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();
       }
     }));

然而不用Guava的函数式编程工具,你只需要:

Multiset<Integer> lengths = HashMultiset.create();
for (String string : strings) {
  if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) {
    lengths.add(string.length());
  }
}

哎哟,居然比用函数式还简短,而且很好懂。这也是为什么Guava呼吁大家不要用它的函数式编程工具类了。除非以下几种情况: - 使用函数式编程以后,真的能缩短代码行数,那些用静态import以及另开一个类用常量保存functions的方法,并不能缩短代码行数,那些移到别的地方去的代码,也要算上行数才行。 - 为了效率,比如有些方法会返回给你一个视图,为了使用这些返回视图的高效方法,你可以使用函数式编程。但是,正如《Effective Java》这本书说的,任何优化不要想当然,等你用完你认为会快的方法后,最好做个测试,用数字来证明你是对的。

Functions和predicates

讲真我一开始接触到Guava的函数式时,直观的感觉就是这玩意儿好晦涩,什么玩意儿,为什么要从右往左读等等。 说实话在JDK7以及以下的版本上,很难见到更便利更新颖的写法,因此Guava的函数式实现只能勉强接受了。 主要是两个接口,分别简单介绍一下。 ### Function 第一个接口名字很好懂,就叫函数。 函数式编程其实就是将我们在编程的过程中,遇到的y=f(x)这样的逻辑关系编写成一个一个的函数,并且可以作为参数、变量来使用。 一个函数最主要的就是由三部分组成:入参x,函数逻辑,结果y 所以Guava的函数接口总结下来就是这样:

Function<String, Integer> lengthFunction = new Function<String, Integer>() {
  public Integer apply(String string) {
    return string.length();
  }
};

在这里,我们的入参就是一个String,而结果就是一个Integer,函数的逻辑就是匿名内部类apply方法实现的string.length()。 可以看到,由于实现一个函数,我们至少要写5行代码,而实际上,真正使用的时候,少不了函数相互之间的连接、嵌套等,这导致使用函数式编程的时候,会有大量此类定义一个函数的模板代码产生。 当我们使用的时候,可以把这个函数传给Iterables.transform()这样的方法,然后Guava就会针对一个可迭代对象的每个元素使用函数,得到结果,最终生成一个经过该函数转换的可迭代对象。 比如:

List<String> strings = Lists.newArrayList("acb","aaad");
Iterable<Integer> result = Iterables.transform(strings,lengthFunction);//result:[3,4]

Predicate

这个是用来做条件判断的特殊函数式结构,它的返回值固定成了boolean值。如下:

Predicate<String> allCaps = new Predicate<String>() {
  public boolean apply(String string) {
    return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
  }
};

这样我们在过滤一个数组的时候,传入这个函数,Guava就会自动根据它返回的结果,决定要不要留下数组中的每个元素:

List<String> strings = Lists.newArrayList("dbc","DBC");
Iterable<String> result = Iterables.filter(strings,allCaps);//result:["DBC"]

所有的Function和Predicate都支持嵌套调用,只要使用FunctionsPredicates中提供的方法即可。Predicates还提供了一些预置的逻辑判断供我们使用,比如containsnotin之类的。