前言
今天主要讲解 Java 8 新特性 - 函数式接口,虽然现在已经都到 JDK 16了,但相信目前使用 Java 8 的同学应该还是占多数吧。
那究竟什么是函数式接口?
简单概括:有且仅有一个抽象方法,但可以有多个非抽象方法的接口。
Java 8 中新增 @FunctionalInterface
注解,而这个注解主要作用是定义该接口为函数式接口。
定义一个函数式接口 @FunctionalInterface
注解不是必须要添加的,只要该接口符合函数式接口的定义即可,但如果接口不符合函数式接口的定义,但又加上 @FunctionalInterface
注解,编译器会直接报错。
正文
在 Java 8 中新增了很多抽象的函数式接口,比如我们常见的 Supplier、Consumer、Function 等,这些函数式接口都定义在 java.util.function 包下。
下面就几个比较常见的接口简单介绍一下:
Consumer
Consumer,消费型接口,接收传入的泛型参数,执行 accept()
方法。
根据接口的命名和定义的方法可以知道, Consumer 接口主要场景:处理接收的输入参数,类似消费者的角色。
源码定义:
@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); };
}
}
使用姿势:
public static void main(String[] args) {
Consumer<String> consumer = (str) -> System.out.println(str);
// 1. 直接执行accept方法
consumer.accept("hello world!");
// 2. 配合stream使用
Stream.of("1", "2", "3").forEach(consumer);
}
于此类似的还有 BiConsumer
、IntConsumer
、DoubleConsumer
、LongConsumer
等接口,区别的是定义的传参类型不一样或者可以传入多个泛型参数。
Supplier
Supplier,提供型接口,调用 get()
方法返回泛型 T 参数
根据接口的命名和定义的方法可以知道, Supplier 接口主要场景:数据生成,类似提供者的角色。
源码定义:
@FunctionalInterface
public interface Supplier<T> {
T get();
}
使用姿势:
public static void main(String[] args) {
Supplier<Integer> supplier = () -> new Random().nextInt();
System.out.println(supplier.get());
}
于此类似的还有 IntSupplier
、DoubleSupplier
、LongSupplier
、BooleanSupplier
等接口,返回不同的数据类型。
Predicate
Predicate,英文翻译过来是谓语、断言的意思,其实就是一个返回 boolean 的判断型接口。
源码定义:
@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);
}
}
使用姿势:
public static void main(String[] args) {
Predicate<Integer> predicate = (i) -> i > 5;
System.out.println(predicate.test(6));
System.out.println(predicate.test(1));
}
Function
Function,功能型接口,接收泛型 T 对象,返回泛型 R 对象。
主要用于数据的处理转换,将输入数据处理后,返回特定需要的输出数据。
源码定义:
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;
}
}
可以看到方法 apply(T t)
是数据转换的核心函数,但同时 Function 也提供了 compose 和 andThen 两个默认方法。
- compose:先执行参数的 apply,再执行调用者的 apply
- andThen:先执行调用者,再执行参数,和compose相反
光看文字可能有点懵,直接看下面的例子可能就一目了然
使用姿势:
public static void main(String[] args) {
Function<Integer, Integer> function = i -> i * 10;
System.out.println(function.apply(2));
// 先执行 2*10 再执行 20+1
Function<Integer, Integer> com = i -> 1 + i;
System.out.println(com.compose(function).apply(2));
// 先执行 2+2 再执行 4*10
Function<Integer, Integer> andThen = i -> 2 + i;
System.out.println(andThen.andThen(function).apply(2));
}
于此类似的还有 BiFunction、
IntFunction、DoubleFunction
、LongFunction
等接口,区别的是定义的传参类型不一样或者可以传入多个泛型参数。
总结
在 java.util.function
包中定义了很多函数式抽象接口,至于它们的用法,其实熟练之后无非就是就这么几种。
在许多的框架源码中,都用到了这一个特性,因为它不仅大大提高我们代码的简洁性,同时也提高了方法的复用性。
普通的改变,将改变普通
我是宅小年,一个在互联网低调前行的小青年
关注公众号「宅小年」,个人博客 📖 edisonz.cn,阅读更多分享文章