1. java8内置的函数式接口
jdk8中,在java.util.function包中提供了大量的函数式接口,这些接口都是函数式接口,且都使用 @FunctionalInterface 标注。
java8中内置的函数式接口分类:
(1)消费型接口 Consumer
(2)供给型接口 Supplier
(3)函数型接口 Function
(4)断言型接口 Predicate
消费型接口 Consumer.
给定T类型的参数,进行逻辑处理,不返回任何值,直接消费。
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); };
}
}
供给型接口 Supplier.
不输入任何值,直接得到一个T类型的输出。
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
函数型接口 Function.
给定T类型的参数,返回R类型的结果。
主要用来对输入进行逻辑处理,然后进行输出。
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;
}
}
断言型接口 Predicate.
给定T类型的参数,计算出断言式的boolean结果。
主要用来对输入做判断,输出判断结果。
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);
}
}
下面我们逐个分析。
2. 消费型接口 Consumer
2.1 Consumer接口
(1)源码:
package java.util.function;
import java.util.Objects;
// 函数式接口
// 表示“接受一个参数输入且不需要有任何返回值的操作。”
// 不同于其他的函数式接口,Consumer期望通过方法的实现来执行一段不需要返回值的逻辑。
@FunctionalInterface
public interface Consumer<T> {
// 抽象方法,接口一个T类型参数且没有返回值
void accept(T t);
// 默认方法,提供链式调用方式执行。
// 执行流程:先执行本身的accept方法,再执行传入参数after的accept方法。
// 该方法会抛出NullPointException异常。
// 如果在执行调用链时出现异常,会将异常传递给调用者,且发生异常后的after将不会再调用。
// 实际上,这个andThen方法就干了一件事:返回了一个新的Consumer对象。
// 这个新的Consumer对象复写的accept方法的逻辑分两步:先执行当前调用者的accept方法,再执行传入的after的accept方法。
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
(2)案例:
package zeh.myjavase.code42java8.demo05;
import java.util.function.Consumer;
/**
* 功能描述:消费型函数式接口 Consumer
*
* @ClassName ConsumerRun
* @Author zhaoerhu
* @Date 2023/3/21 21:06
*/
public class ConsumerRun {
public static void main(String[] args) {
testConsumer();
testAndThen();
}
// 测试Consumer接口,将一个数字+1后输出
public static void testConsumer() {
// lambda表达式创建Consumer对象
Consumer<Integer> addOne = num -> System.out.println(num + 1);
addOne.accept(20);
}
// Consumer接口还提供了一个andThen的默认方法。
// 该方法表示,先执行当前Consumer对象的accept方法,再执行传入参数after的accept方法。
// 并且执行完毕返回一个Consumer对象以支持链式编程。
public static void testAndThen() {
// 使用方法引用输出参数
Consumer<String> consumer1 = name -> System.out.println("consumer1:" + name);
Consumer<String> consumer2 = name -> {
System.out.println("consumer2:" + name);
// 手动抛出一个异常
throw new NullPointerException();
};
Consumer<String> consumer3 = name -> System.out.println("consumer3:" + name);
// consumer1.andThen(consumer2):
// 返回一个新的Consumer对象,该对象实现的accept方法逻辑是:先执行consumer1实现的accept方法,再执行consumer2的accept方法。
// consumer1.andThen(consumer2).andThen(consumer3):
// 然后链式起来,实际上返回了一个全新的Consumer对象,该对象实现的accept方法的逻辑是,先执行consumer1的accept,再执行consumer2的accept,最后执行consumer3的accept方法。
// 这里的andThen要好好接一下,它的本质并不是在触发accept方法的执行,而是返回一个新的Consumer对象。
// 也就是说,通过链式的调用andThen方法,只不过是在创建一个新的Consumer对象。
// 只不过andThen方法返回的Consumer对象,它实现的accept方法逻辑,实际上是将多个consumer对象的accept方法调用连接起来了而已。
// 它的本质还是一段逻辑体,是一个Lambda表达式,是一个函数式接口的对象。
// 无论多少个consumer对象通过andThen方法组合起来,它最终只会返回一个新的Consumer对象,这个新的Consumer对象代表着accept方法的具体执行逻辑:
// 那就是,按照链式传入的顺序来顺序化执行多个consumer对象的accept方法,先执行第一个consumer的accept,再执行第二个consumer,以此类推。
// 此处的3个consumer对象最终组合了一个新的consumer对象,但是它被触发时接收的实际参数其实只能是一个,就是下面传入的eric。
// 因此,无论多少个Consumer对象通过andThen进行组合,每一个Consumer对象实现的accept方法接收的参数类型得统一。
Consumer<String> complexConsumer = consumer1.andThen(consumer2).andThen(consumer3);
// 在此处传入实际数据,触发accept方法的执行。
// 所以说,complexConsumer对象实际上只是实现了此处要执行的accept方法的执行逻辑而已。
// 真正的触发执行还需要调用accept方法。
// 从输出结果看:andThen返回的链式节点中,只要有一个consumer的accept执行抛出了异常,则后续的consumer对象将不再执行。
complexConsumer.accept("eric");
}
}
输出:
21
consumer1:eric
consumer2:eric
Exception in thread "main" java.lang.NullPointerException
at zeh.myjavase.code42java8.demo05.ConsumerRun.lambda$testAndThen$2(ConsumerRun.java:36)
at java.util.function.Consumer.lambda$andThen$0(Consumer.java:65)
at java.util.function.Consumer.lambda$andThen$0(Consumer.java:65)
at zeh.myjavase.code42java8.demo05.ConsumerRun.testAndThen(ConsumerRun.java:51)
at zeh.myjavase.code42java8.demo05.ConsumerRun.main(ConsumerRun.java:16)
Process finished with exit code 1
2.2 BiConsumer接口
(1)源码:
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface BiConsumer<T, U> {
// 与Consumer接口相比,BiConsumer接口可以同时接收两个参数进行处理
void accept(T t, U u);
// andThen用于链式组合多个BiConsumer对象,生成一个新的BiConsumer
default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after);
return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
}
(2)案例:
package zeh.myjavase.code42java8.demo05;
import java.util.function.BiConsumer;
// BiConsumer接口,可以接收2个参数的Consumer消费型接口
public class BiConsumerRun {
public static void main(String[] args) {
testBiConsumer();
testAndThen();
}
public static void testBiConsumer() {
BiConsumer<String, Integer> biConsumer = (name, num) -> System.out.println(name + "在放学路上捡到了" + num + "毛钱!");
biConsumer.accept("eric", 5);
}
public static void testAndThen() {
// 无论多少个BiConsumer对象通过andThen在组合逻辑产生一个新的BiConsumer,它最终只能接收一份实际入参。
// 因此,每个biConsumer对象实现的accept方法的逻辑都要符合这一份实际入参的语义。
// 下面分别是三个biConsumer对象实现的语义,尽管展示了不同的逻辑,但操作加工的实际参数,其实都是"李宁"和10
// 李宁昨天花了10块钱请我吃饭
// 李宁为了10块钱把我门牙打掉了
// 一件李宁衣服卖10块钱
BiConsumer<String, Integer> biConsumer1 = (name, num) -> System.out.println(name + "昨天花了" + num + "块钱请我吃饭");
BiConsumer<String, Integer> biConsumer2 = (name, num) -> System.out.println(name + "为了" + num + "块钱把我门牙打掉了");
BiConsumer<String, Integer> biConsumer3 = (a, b) -> System.out.println("一件" + a + "衣服卖" + b + "块钱");
biConsumer1.andThen(biConsumer2).andThen(biConsumer3).accept("李宁", 10);
}
}
输出:
eric在放学路上捡到了5毛钱!
李宁昨天花了10块钱请我吃饭
李宁为了10块钱把我门牙打掉了
一件李宁衣服卖10块钱
Process finished with exit code 0
2.3 IntConsumer接口
(1)源码:
package java.util.function;
import java.util.Objects;
// 很简单了,是Consumer的一个特殊定制化,只接受一个int参数
@FunctionalInterface
public interface IntConsumer {
void accept(int value);
default IntConsumer andThen(IntConsumer after) {
Objects.requireNonNull(after);
return (int t) -> { accept(t); after.accept(t); };
}
}
(2)案例:
package zeh.myjavase.code42java8.demo05;
import java.util.function.IntConsumer;
// 只接受一个int参数的Consumer接口
public class IntConsumerRun {
public static void main(String[] args) {
testIntConsumer();
testAndThen();
}
public static void testIntConsumer() {
IntConsumer intConsumer = num -> System.out.println("我的尺寸只有" + num + "公分");
intConsumer.accept(10);
}
public static void testAndThen() {
IntConsumer intConsumer1 = num -> System.out.println("他的身高是" + num + "公分");
IntConsumer intConsumer2 = num -> System.out.println("这件外套" + num + "块钱");
IntConsumer intConsumer3 = num -> System.out.println("警报响了" + num + "次");
intConsumer1.andThen(intConsumer2).andThen(intConsumer3).accept(176);
}
}
运行:
我的尺寸只有10公分
他的身高是176公分
这件外套176块钱
警报响了176次
Process finished with exit code 0
类似的,单参数Consumer还有 DoubleConsumer 和 LongConsumer ,分别接受一个double参数和一个long参数。
2.4 ObjIntConsumer接口
(1)源码:
package java.util.function;
// 接受1个T类型的参数对象和一个int参数
@FunctionalInterface
public interface ObjIntConsumer<T> {
// 和BiConsumer相比,该接口显式接受一个对象参数,和一个int参数
// 从接口名称就能看出来,ObjIntConsumer,指的是一个Object和一个Int
// 与BiConsumer不同的是,一个对象和一个int参数,不允许链式组合使用andThen。
// 其实从使用场景来理解,同时传递一个对象和一个int参数,一般不会用于链式组合场景。
// 如果想接受两个参数用于andThen链式组合,那么请直接使用BiConsumer接口即可、
void accept(T t, int value);
}
(2)案例:
package zeh.myjavase.code42java8.demo05;
import java.util.function.ObjIntConsumer;
// 接受2个参数,第1个参数是对象,第2个参数是int的Consumer
public class ObjIntConsumerRun {
public static void main(String[] args) {
testObjIntConsumer();
}
// ObjIntConsumer 接口不支持andThen操作
public static void testObjIntConsumer() {
ObjIntConsumer<String> objIntConsumer = (name, num) -> System.out.println(name + "花了" + num + "块钱就追到了女朋友");
objIntConsumer.accept("张三", 100);
}
}
运行:
张三花了100块钱就追到了女朋友
Process finished with exit code 0
类似的,双参数Consumer还有 ObjDoubleConsumer 和 ObjLongConsumer ,ObjDoubleConsumer接受一个对象和一个double参数,ObjLongConsumer接受一个对象和一个long对象。
文档信息
- 本文作者:Marshall