函数式工具
说明
Java 8包含java.util.function
和java.util.stream
包,其在该语言级别(JDK 8)取代了Guava的函数式编程类。
虽然在Java 8之前的版本Guava的函数式工具是可用的,但是在没有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.javaUpperCase().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.javaUpperCase().matchesAllOf(string)) {
lengths.add(string.length());
}
}
即使使用静态引入,即使Function和Predicate声明被移动到不同的文件,第一个实现不是太简洁,可读和高效的。
命令式代码应该是你的默认代码,也是从Java 7开始是你的第一选择。你不应该使用函数式语法除非你完全确定以下其中一种情况:
- 使用函数式语法将导致节约你整个项目的代码行数。在上面的示例中,“functional”版本使用 11行,硬编码版本使用6行。将函数移动到另外一个文件,或者一个常量并没有用。
- 为了提高效率,你需要一个转换的集合的延迟计算视图,不能满足于显性的计算集合。除此之外,你已经读过或者在读Effective Java,条目55并且除了遵循这些说明之外,你实际进行基准测试,以证明此版本更快并且可以引用数字来证明它。
请确信,当使用Guava的函数式工具,做这些事情的传统的硬式编码方式不会更加可读。试着把它写出来。有那么糟糕吗?这种方式是否比你即将尝试的荒谬和笨拙的函数方法更具有可读性?
Functions和Predicates
此主题只讨论这些直接处理Function
和Predicate
的Guava特性。一些其他工具与“函数式风格”相关联,例如连接和其他方法在恒定时间内返回视图的方法。尝试到集合实用工具章节看一看。
Guava提供两个最基本的“函数式”接口:
Function<A, B>
,它有一个单独的方法B apply(A input)
。Function
的实例通常希望是引用透明的 – 无副作用 – 并且equels一致,也就是a.equals(b)
意味着function.apply(a).equals(function.apply(b))
。Predicate<T>
,它有一个单独的方法boolean apply(T input)
。Predicate
的实例通常希望是无任何副作用并且equals一致。
特殊的predicates
字符有他们自己的Predicate
专用版本:CharMatcher
,它对这些需求通常更高效,更加有用。CharMatcher
已经实现Predicate<Character>
并且可以相应地使用,而从Predicate
到CharMatcher
的转换可以使用CharMatcher.forPredicate
。查阅CharMatcher
章节了解更多详情。
除此之外,对于可比较的类型和基于比较的predicates,大多数需求可以使用Range
类型来满足,它实现了不可变间隔(interval)。Range
类型实现Predicate
,测试范围内的包含。例如,Range.atMost(2)
是一个完美有效地Predicate<Integer>
。使用range的更多细节可以在Range
章节发现。
操作Functions和Predicates
简单的Function
构造和操作方法在Functions
中提供,包括:
查看Javadoc了解更多详情。
在Predicates
有非常多的构造和可用的操作方法,但是一个例子包含:
instanceOf(Class)
assignableFrom(Class)
contains(Pattern)
in(Collection)
isNull()
alwaysFalse()
,alwaysTrue()
equalTo(Object)
compose(Predicate, Function)
and(Predicate...)
,or(Predicate...)
,not(Predicate)
查看Javadoc了解更多详情。
使用
Guava提供许多工具通过使用functions和predicates操作集合。这些通常能在集合工具类Iterables
,Lists
,Sets
,Maps
,Multimaps
等等中找到。
Predicates
predicates最基本的使用是过滤集合。所有的Guava过滤器方法返回视图:
集合类型 | 过滤器方法 |
---|---|
Iterable | Iterables.filter(Iterable, Predicate) ,FluentIterable.filter(Predicate) |