前言
函数式接口(Functional Interface
)是 Java 8 对一类特殊类型的接口的称呼,点击打开官方文档,就可以看到 java.util.function
包的介绍:“Functional interfaces provide target types for lambda expressions and method references.”以及其中的所有接口的汇总信息。
接口分类
粗略观察都不难发现接口的命名都是“数据类型” + “接口类型”的形式,而接口类型可以大致分为以下 4 类:
接口 | 类型 | 说明 |
---|---|---|
Consumer | 消费型接口 | 接收一个参数,但不返回结果 |
Function<T, R> | 函数型接口 | 接收一个参数,返回生成结果 |
Predicate | 断言型接口 | 接收一个参数,返回 boolean 值 |
Supplier | 供给型接口 | 不接收参数,但返回结果 |
除此以外,还有一些:
BiFunction<T, U, R>
:接受两个不同类型的参数(泛型 T 类型和 泛型 U 类型),然后返回一个其他类型的值(泛型 R 类型)。UnaryOperator<T>
:继承于Function<T, T>
,接收一个参数(泛型 T 类型),返回与参数相同类型的结果。接口使用
Consumer 接口
源码如下所示。
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Consumer<T> { void accept(T t); default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
只有两个方法,一个是抽象方法
accept()
,传入一个任意类型参数,无返回值,可以用于 Lambda 表达式和方法引用;另一个是默认方法andThen()
,可以传入一个 Consumer,返回组合了两个 Consumer 后的 Consumer ,传入的 Consumer 不能为 null,否则会得到 NullPointerException。使用示范
注意:当调用
andThen()
方法的时候,并不是执行accept(t); after.accept(t);
这段代码,而是返回了一个 Consumer 接口,这个返回的结果再调用accept()
方法就执行处理逻辑:当前 Consumer 先执行accept()
方法,然后再让 after 这个 Consumer 执行accept()
方法。public class ConsumerFunctionTest { public static void main(String[] args) { // 1.accept() 接收 String 类型 Consumer<String> lengthConsumer = s -> System.out.println("字符串的长度:" + s.length()); lengthConsumer.accept("newabug.top"); // 2.accept() 接收 int 类型 Consumer<Integer> modifyTheValue = x -> System.out.println("接收的是" + x + ",输出的是" + (x * 2)); modifyTheValue.accept(10); // 3.lengthConsumer 调用 andThen() 返回了一个 Consumer 接口 Consumer<String> printConsumer = lengthConsumer.andThen(System.out::println); // 这个返回的 Consumer 接口再根据顺序执行 accept() printConsumer.accept("www.baidu.com"); // 4.也可以这么写 Consumer<Integer> changeTheValue = y -> System.out.println("去年是" + y + "年,现在是" + (y + 1)); changeTheValue.andThen(modifyTheValue).accept(2021); } }
更多
我们使用
Stream.forEach(Consumer<? super T> action)
方法的时候,该方法就是使用了 Consumer 接口作为参数。除了打印信息,一般我们对于集合中的对象的某些数据需要更改,也经常使用 forEach 遍历,然后对于每个对象做一些业务操作。
除了 Consumer之外,Java 8 还提供了适用于不同类型的 Consumer,如下表所示。 接口名称 方法 描述 BiConsumer <T, U>
void accept(T t, U u) 传入两个任意类型参数,无返回值 DoubleConsumer void accept(double value) 传入一个 double 参数,无返回值 IntConsumer void accept(int value) 传入一个 int 参数,无返回值 LongConsumer void accept(long value) 传入一个 long 参数,无返回值 ObjDoubleConsumer <T>
void accept(T t, double value) 传入一个任意类型参数,一个 double 参数,无返回值 ObjIntConsumer <T>
void accept(T t, int value) 传入一个任意类型参数,一个 int 参数,无返回值 ObjLongConsumer <T>
void accept(T t, long value) 传入一个任意类型参数,一个 long 参数,无返回值 Function 接口
源码如下所示。
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity() { return t -> t; } }
Function 接口包含的单一抽象方法为
apply(T t)
,它可以将 T 类型的泛型输入参数转换为 R 类型的泛型输出值。
默认方法有两个,一个是compose()
,另一个是andThen()
,传入的参数都是 Function 类型,都不能为 null,返回的结果都是 Function 类型组合函数,区别是compose(before)
方法返回一个先执行 before 函数对象 apply() 方法,再执行当前函数对象 apply() 方法的函数对象,而andThen(after)
方法返回一个先执行当前函数对象 apply() 方法, 再执行 after 函数对象 apply() 方法的函数对象。
静态方法identity()
返回一个始终返回其输入参数的函数。使用示范
public class FunctionTest { public static void main(String[] args) { // 1.使用 R apply(T t),输入 String 类型,得到其长度 Function<String, Integer> getStrLength = String::length; int length = getStrLength.apply("newabug.top"); System.out.println("字符串的长度:" + length); // 2.使用 compose(Function before),返回的也是 Function 的结果 Function<Integer, Integer> multiplyValue = (value) -> value * 2; Function<Integer, Integer> addValue = (value) -> value + 3; // addValue 作为 before 参数传入 compose() 方法 Function<Integer, Integer> addThenMultiply = multiplyValue.compose(addValue); Integer result1 = addThenMultiply.apply(1); // 根据结果可知,是先执行 (1 + 3) 再执行 * 2 System.out.println(result1); // 3.使用 andThen(Function after) Function<Integer, Integer> multiplyThenAdd = multiplyValue.andThen(addValue); Integer result2 = multiplyThenAdd.apply(1); // 根据结果可知,是先执行 1 * 2 再执行 + 3 System.out.println(result2); // 4.使用 identity(),将 Stream 转换成容器或 Map Stream<String> stream = Stream.of("今天的", "风", "甚是喧嚣", "11:22:38"); Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length)); for (Integer value : map.values()) { System.out.println(value); } } }
更多
除了 Function<T,R> 之外,还有各种适用于不同类型的,如下表所示。
接口名称 方法 描述 BiFunction <T, U, R>
R apply(T t, U u) 传入两个任意类型参数,返回值类型为泛型 R DoubleFunction <R>
R apply(double value) 传入一个 double 参数,返回值类型为泛型 R DoubleToIntFunction int applyAsInt(double value) 传入一个 double 参数,返回值类型为 int DoubleToLongFunction long applyAsLong(double value) 传入一个 double 参数,返回值类型为 long IntFunction <R>
R apply(int value) 传入一个 int 参数,返回值类型为泛型 R IntToDoubleFunction double applyAsDouble(int value) 传入一个 int 参数,返回值类型为 double IntToLongFunction long applyAsLong(int value) 传入一个 int 参数,返回值类型为 long LongFunction <R>
R apply(long value) 传入一个 long 参数,返回值类型为泛型 R LongToDoubleFunction double applyAsDouble(long value) 传入一个 long 参数,返回值类型为 double LongToIntFunction int applyAsInt(long value) 传入一个 long 参数,返回值类型为 int ToDoubleBiFunction <T, U>
double applyAsDouble(T t, U u) 传入参数类型分别为泛型 T, U,返回值类型为 double ToDoubleFunction <T>
double applyAsDouble(T value) 传入参数类型为泛型 T,返回值类型为 double ToIntBiFunction <T, U>
int applyAsInt(T t, U u) 传入参数类型分别为泛型 T, U,返回值类型为 int ToIntFunction <T>
int applyAsInt(T value) 传入参数类型为泛型 T,返回值类型为 int ToLongBiFunction <T,U>
long applyAsLong(T t, U u) 传入参数类型分别为泛型 T, U,返回值类型为 long ToLongFunction <T>
long applyAsLong(T value) 传入参数类型为泛型 T,返回值类型为 long Predicate 接口
源码如下所示。
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Predicate<T> { boolean test(T t); default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate<T> negate() { return (t) -> !test(t); } default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); }
Predicate 接口包含的单一抽象方法为
boolean test(T t)
,它传入一个泛型参数并返回 true 或 false。
默认方法有三个,返回值都是Predicate<T>
,分别是and(Predicate<? super T> other)
、negate()
、or(Predicate<? super T> other)
,使用and()
方法和or()
方法都可以让前后两个判断条件一起生效,只不过判断逻辑不同:前者为逻辑与“&&”,存在短路特性,需要所有条件都满足;后者为逻辑或“||”,多个条件只要一个满足即可。negate()
方法会返回一个与指定判断相反的 Predicate,判断逻辑为逻辑非“!”。
静态方法isEqual(Object targetRef)
判断两个对象是否相同,先判断对象是否为 null,不为 null 的话再使用equals()
方法进行比较。使用示范
public class PredicateFunctionTest { public static void main(String[] args) { // 1.使用 test(T t),返回布尔值 Predicate<String> strLength = s -> s.length() > 5; System.out.println(strLength.test("java")); System.out.println(strLength.test("Python")); // 2.使用 and(Predicate<? super T> other),拼接判断条件 Predicate<String> startsWithA = (text) -> text.startsWith("A"); Predicate<String> endsWithX = (text) -> text.endsWith("s"); // 条件判断为 && Predicate<String> composed = startsWithA.and(endsWithX); String input = "A Boy Can Do Everything For Girls"; boolean ifSimp = composed.test(input); System.out.println("是否舔狗:" + ifSimp); // 3.使用 or(Predicate<? super T> other),拼接判断条件 Predicate<String> composed2 = startsWithA.or(endsWithX); // 条件件判断为 || String input2 = "But girls prefer millionaires"; boolean ifRefuse = composed2.test(input2); System.out.println("是否被拒绝:" + ifRefuse); // 4.使用 negate() List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Predicate<Integer> isEven = i -> (i & 1) == 0; // 返回一个与指定判断相反的 Predicate Predicate<Integer> isOdd = isEven.negate(); List<Integer> evenNumbers = list.stream().filter(isEven).collect(Collectors.toList()); List<Integer> oddNumbers = list.stream().filter(isOdd).collect(Collectors.toList()); System.out.println("偶数:" + evenNumbers); System.out.println("奇数:" + oddNumbers); // 5.使用 isEqual() List<String> programingList = Arrays.asList("Java", "Go", "C++", "Python", "JavaScript", "PHP"); Predicate<String> isEqual = (n) -> Predicate.isEqual("PHP").test(n); programingList.stream().filter(isEqual).forEach((n) -> System.out.println("相等的词语:" + n)); } }
更多
Predicate 的
and()
、or()
、negate()
方法可以随意组合 Predicate,还有其他一些用法:Optional.filter(Predicate<? super T> predicate)
:如果值存在且匹配某个给定条件,则返回描述该值的 Optional,否则返回一个空的 Optional。Collection.removeIf(Predicate<? super E> filter)
:删除集合中所有满足给定条件的元素。Stream.allMatch(Predicate<? super T> predicate)
:如果流的所有元素均满足给定的条件,则返回 true。
自 Java 11 后,Predicate 接口新增了一个静态方法 not(Predicate<? super T> target)
,它返回一个 Predicate,该 Predicate 是所提供的 target 的否定。
static <T> Predicate<T> not(Predicate<? super T> target) {
Objects.requireNonNull(target);
return (Predicate<T>)target.negate();
}
Predicate 其他类型的接口如下表所示。
接口名称 | 方法 | 描述 |
---|---|---|
BiPredicate<T,U> | boolean test(T t, U u) | 传入参数类型分别为泛型 T, U,返回值类型为 boolean |
DoublePredicate | boolean test(double value) | 传入一个 double 参数,返回值类型为 boolean |
IntPredicate | boolean test(int value) | 传入一个 int 参数,返回值类型为 boolean |
LongPredicate | boolean test(long value) | 传入一个 long 参数,返回值类型为 boolean |
Supplier 接口
源码如下所示。
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
是的,Supplier 接口相当简单,它不包含任何静态或默认方法,只有一个抽象方法 T get()
。
使用示范
Supplier 的一种简单应用是 Math.random()
方法,它不传入参数且返回 double 型随机数。
public class SupplierFunctionTest {
public static void main(String[] args) {
// 1.返回随机数
Supplier<Double> randomSupplier = Math::random;
System.out.println("生成的随机数:" + randomSupplier.get());
// 2.返回当前时间
Supplier<LocalDateTime> timeSupplier = LocalDateTime::now;
System.out.println("当前的时间:" + timeSupplier.get());
}
}
更多
Java 8 还提供了指定类型的 Supplier,有 BooleanSupplier, DoubleSupplier, IntSupplier, LongSupplier,它们也只有一个获取对应类型返回值的方法 getAsxxx()
。
参考
菜鸟教程
云+社区 - chenchenchen