本章主要介绍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》这本书说的,任何优化不要想当然,等你用完你认为会快的方法后,最好做个测试,用数字来证明你是对的。
讲真我一开始接触到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]
这个是用来做条件判断的特殊函数式结构,它的返回值固定成了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都支持嵌套调用,只要使用Functions
和Predicates
中提供的方法即可。Predicates
还提供了一些预置的逻辑判断供我们使用,比如contains
、not
、in
之类的。