上一篇中介绍的函数式接口的抽象方法是Lambda表达式的函数描述符,Lambda表达式需要有与之相匹配的函数式接口才能正常工作,那么在java8中,java的设计者为我们提供了哪些常用的函数式接口?
java.util.function.Predicate< T >
Predicate(谓词)函数式接口定义了一个test抽象方法,它接受泛型参数T并返回布尔值,其定义如下:
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
}
谓词(predicate)在数学上常常用来代表一个类似函数的东西,它接受一个参数值,并返回true或false。
如果定义一个接受String对象的Lambda表达式,代码如下:
//根据条件(行为)p过滤集合,符合条件p的加入过滤结果中
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> results = new ArrayList<>();
for(T s: list){
if(p.test(s)){
results.add(s);
}
}
return results;
}
//Lambda表达式,判断非空
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
//从集合中过滤出非空的字符串
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
java.util.function.Consumer< T >
Consumer翻译成中文是“消费者”的意思,如果从输入输出的角度来考虑,消费这一行为有输入但没有输出,因为输入的东西被消费掉了,对应到java里的函数就是一个有参数但没有返回值的函数,这个函数就是函数式接口Consumer的抽象方法,方法名叫accept,接收一个泛型参数T,无返回(void),定义如下:
@FunctionalInterface
public interface Consumer<T>{
void accept(T t);
}
应用如下:
public static <T> void forEach(List<T> list, Consumer<T> c){
for(T i: list){
c.accept(i);
}
}
//Lambda是函数式接口Consumer中抽象方法accept的实现
forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));
这里传递给forEach方法的第二个参数:Lambda表达式,就相当于实现了函数式接口Consumer中抽象方法accept.
java.util.function.Function< T, R >
定义:
@FunctionalInterface
public interface Function<T, R>{
R apply(T t);
}
从函数式接口Function的定义中可以看出,抽象方法apply是将泛型参数对象T转换成了泛型R对象,常用于将输入信息映射成另一种类型的输出。示例如下:
public static <T, R> List<R> map(List<T> list,Function<T, R> f) {
List<R> result = new ArrayList<>();
for(T s: list){
result.add(f.apply(s));
}
return result;
}
//将输入的字符串集合映射成字符串长度的整形集合
List<Integer> l = map(Arrays.asList("lambdas","in","action"),(String s) -> s.length());
同样,上面代码传递给map方法的第二个参数:lambda表达式,是Function方法apply的实现。
原始类型特化
前方介绍的三个泛型函数式接口: Predicate< T >、 Consumer< T >和Function< T,R >,只能绑定到引用类型,对于基本类型,如int、double等,虽然也能使用包装类型Intger\Double等来解决,但会涉及到自动装箱操作,当处理一定量的数据时,会带来性能问题,因此java8针对原始类型特化了一些函数式接口,专门用来处理原始类型数据,避免装箱与拆箱带来的性能问题。如:IntPredicate、LongPredicate等,其使用跟普通的函数式接口一样,只是针对了特定的数据类型。
java8还预定义了其它类型的函数式接口,如Supplier(与Consumer相对应)、UnaryOperator等,将它们总结如下:
函数式接口 | 函数描述符 | 原始类型特化 |
---|---|---|
Predicate< T > | T->boolean | IntPredicate,LongPredicate, DoublePredicate |
Consumer< T > | T->void | IntConsumer,LongConsumer, DoubleConsumer |
Function< T,R > | T->R | IntFunction< R >,IntToDoubleFunction,IntToLongFunction,LongFunction< R >,LongToDoubleFunction,LongToIntFunction,DoubleFunction< R >,ToIntFunction< T >,ToDoubleFunction< T >,ToLongFunction< T > |
Supplier< T > | ()->T | BooleanSupplier,IntSupplier, LongSupplier,DoubleSupplier |
UnaryOperator< T > | T->T | IntUnaryOperator,LongUnaryOperator,DoubleUnaryOperator |
BinaryOperator< T > | (T,T)->T | IntBinaryOperator,LongBinaryOperator,DoubleBinaryOperator |
BiPredicate< L,R > | (L,R)->boolean | |
BiConsumer< T,U > | (T,U)->void | ObjIntConsumer< T >,ObjLongConsumer< T >,ObjDoubleConsumer< T > |
BiFunction< T,U,R > | (T,U)->R | ToIntBiFunction< T,U >,ToLongBiFunction< T,U >,ToDoubleBiFunction< T,U > |
一些常用的Lambda表达式与其对应的函数式接口应用举例:
使用案例 | Lambda表达式 | 对应的函数式接口 |
---|---|---|
布尔表达式 | (List list) -> list.isEmpty() | Predicate< List< String > > |
创建对象 | () -> new Apple(10) | Supplier< Apple > |
消费一个对象 | (Apple a) -> System.out.println(a.getWeight()) | Consumer< Apple > |
从一个对象中选择/提取 | (String s) -> s.length() | Function< String, Integer >或ToIntFunction< String > |
合并两个值 | (int a, int b) -> a * b | IntBinaryOperator |
比较两个对象 | (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) | Comparator< Apple >或BiFunction< Apple, Apple, Integer > 或 ToIntBiFunction< Apple, Apple > |