转载

Java8的函数式接口你真的了解吗?

Java8的函数式接口你真的了解吗?
FunctionalInterface

函数式接口

如果你的好奇心使你翻看 Runnable 接口源代码,你会发现该接口被一个 @FunctionalInterface 的注解修饰,这是 Java 8 中添加的新注解,用于表示 函数式接口

Java8的函数式接口你真的了解吗?
FunctionalInterface-Runnable

函数式接口又是什么鬼?

在 Java 8 中,把那些仅有一个抽象方法的接口称为函数式接口。如果一个接口被 @FunctionalInterface 注解标注,表示这个接口被设计成函数式接口,只能有一个抽象方法,如果你添加多个抽象方法,编译时会提示“Multiple non-overriding abstract methods found in interface XXX”之类的错误。

标注为 FunctionalInterface 的接口被称为函数式接口,该接口只能有一个自定义方法,但是可以包括从 object 类继承而来的方法。如果一个接口只有一个方法,则编译器会认为这就是一个函数式接口。是否是一个函数式接口,需要注意的有以下几点:

  • 该注解只能标记在”有且仅有一个抽象方法”的接口上。

  • JDK8 接口中的静态方法和默认方法,都不算是抽象方法。

  • 接口默认继承 java.lang.Object,所以如果接口显示声明覆盖了 Object 中方法,那么也不算抽象方法。

  • 该注解不是必须的,如果一个接口符合”函数式接口”定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。

  • 在一个接口中定义两个自定义的方法,就会产生 Invalid ‘@FunctionalInterface’ annotation; FunctionalInterfaceTest is not a functional interface 错误.

函数式方法又能做什么?

Java8 允许你以 Lambda 表达式的方式为函数式接口提供实现, 通俗的说,你可以将整个 Lambda 表达式作为接口的实现类

除了 Runnable 之外,Java 8 中内置了许多函数式接口供开发者使用,这些接口位于 java.util.function 包中。如:

Java8的函数式接口你真的了解吗?
Functional-Package
name type result desc
Consumer Consumer T -> void 接收 T 对象,不返回值
Predicate Predicate T -> boolean 接收 T 对象并返回 boolean
Function Function<T, R> T -> R 接收 T 对象,返回 R 对象
Supplier Supplier void -> T 提供 T 对象(例如工厂),不接收值
UnaryOperator UnaryOperator T -> T 接收 T 对象,返回 T 对象
BinaryOperator BinaryOperator T, T -> T 接收两个 T 对象,返回 T 对象

如果输入参数是基本类型,为了避免自动拆箱装箱,可以使用其他基本类型的函数接口。

Function

interface Function<T, R> 接口包含一个 apply 方法、两个默认方法( composeandThen )和一个静态方法 identityapply 是接口的基本方法。 composeandThen 是一对儿方法,他们的区别在于执行的顺序不同。

//返回一个先执行before函数对象apply方法再执行当前函数对象apply方法的函数对象
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
}
复制代码
//返回一个先执行当前函数对象apply方法再执行after函数对象apply方法的函数对象。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
}
复制代码
// 根据上面的解释想一下执行结果
Function<Integer, Integer> name = e -> e * 2;
Function<Integer, Integer> square = e -> e * e;
int value = name.andThen(square).apply(3);
int value2 = name.compose(square).apply(3);
//返回一个执行了apply()方法之后只会返回输入参数的函数对象
Object identity = Function.identity().apply("Test");
复制代码

Consumer

interface Consumer<T> 接口包含一个 void accept(T t); 方法、默认方法 andThen .

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}
复制代码

只有一个默认方法也是和它的返回类型有关系,因为返回的是 void。

Predicate

interface Predicate<T> 接口包含一个 boolean test(T t); 方法,三个默认方法( andnegate , or ),还有一个静态方法。 我们也知道 Predicate 接口是返回 boolean 类型的,所以一看就知道是条件判断的。 举几个栗子吧:

String name = "hello";
Predicate<String> predicate = x -> x.equals("hello");
Predicate<String> predicate2 = x -> x.length() < 2;
// and 多个Predicate条件并的关系判断,第一个为false就不往下走了,满足短路原则
System.out.println(predicate.and(predicate2).test(name));
// or 多个Predicate条件或的关系判断,同样满足短路原则
System.out.println(predicate.or(predicate2).test(name));
// negate 取反的意思,就是否的条件判断
System.out.println(predicate.and(predicate2.negate()).test(name));
// isEqual 静态方法,判断是否相等
System.out.println(Predicate.isEqual(name).test(name));
复制代码

Supplier

Supplier 只有一个 get()方法。 我们来看看几个栗子:

Supplier<String> supplier = () -> "hello world";
//get方法不接受参数,返回一个结果
System.out.println("supplier = [" + supplier.get() + "]");

//替代不接受参数的工厂方法
Supplier<Student> studentSupplier = () -> new Student();
System.out.println(studentSupplier.get());

//因为Student的构造方法不接受参数,返回一个结果,符合Supplier接口的要求,可以简写如下:
Supplier<Student> studentSupplier2 = Student::new;
复制代码

总结

函数式接口其实差别不大,只是参数和返回的不同,只要想明白其中的一种,其他的也就懂了。

参考

JDK8 新特性-java.util.function-Function 接口 [1]

Java8 的一些新特性 java.util.function 包 [2]

Java JVM(七):Function,Consumer,Predicate 接口 [3]

求关注、分享、在看!!! 你的支持是我创作最大的动力。

Java8的函数式接口你真的了解吗?
原文  https://juejin.im/post/5eb2e16a5188256d7e066623
正文到此结束
Loading...