Java8新特性探索之函数式接口

作者 | 拼搏吧 , 少年
来源 | urlify.cn/vQry6r
一、为什么引入函数式接口作为Java函数式编程爱好者 , 我们都知道方法引用和 Lambda 表达式都必须被赋值 , 同时赋值需要类型信息才能使编译器保证类型的正确性 。
我们先看一个Lambda代码示例:
x -> x.toString()我们清楚这里返回类型必须是 String , 但 x 是什么类型呢?
Lambda 表达式包含类型推导(编译器会自动推导出类型信息 , 避免了程序员显式地声明) , 编译器必须能够以某种方式推导出 x 的类型以生成正确的代码 。
同样方法引用也存在此问题 , 假设你要传递 System.out :: println 到你正在编写的方法, 你怎么知道传递给方法的参数的类型?
为了解决上述问题 , Java 8 引入了函数式接口 , 在 java.util.function 包 , 它包含一组接口 , 这些接口是 Lambda 表达式和方法引用的目标类型 , 每个接口只包含一个抽象方法 , 称为函数式方法 。 只有确保接口中有且仅有一个抽象方法 , Lambda表达式的类型信息才能顺利地进行推导 。
二、如何使用函数式接口在编写接口时 , 可以使用 @FunctionalInterface 注解强制执行此函数式方法模式:

  1. 在接口上使用注解 @FunctionalInterface, 一旦使用该注解来定义接口 , 编译器将会强制检查该接口是否确实有且仅有一个抽象方法 , 否则将会报错 。 @FunctionalInterfacepublic interface MyFunction { /** * 自定义的抽象方法 */ void run();}
  2. 在函数式接口 , 有且仅有一个抽象方法 , Object的public方法除外@FunctionalInterfacepublic interface MyFunction { /** * 自定义的抽象方法 */ void run(); /** * Object的equals方法 * @param obj * @return */ @Override boolean equals(Object obj); /** * Object的toString方法 * @return */ @Override String toString(); /** * Object的hashCode方法 * @return */ @Override int hashCode();}
  3. 在函数式接口中 , 我们可以使用default修饰符定义默认方法 , 使用static修饰符定义静态方法@FunctionalInterfacepublic interface MyFunction { /** * 自定义的抽象方法 */ void run(); /** * static修饰符定义静态方法 */ static void staticRun() { System.out.println("接口中的静态方法"); } /** * default修饰符定义默认方法 */ default void defaultRun() { System.out.println("接口中的默认方法"); }}
  • 为大家演示下自定义无泛型的函数式接口测试实例:/*** 自定义的无泛型函数式接口*/@FunctionalInterfacepublic interface MyFunction { /** * 自定义的抽象方法 * @param x */ void run(Integer x); /** * default修饰符定义默认方法 * @param x */ default void defaultMethod(Integer x) { System.out.println("接口中的默认方法 , 接收参数是:" + x); }}/*** 测试类*/public class MyFunctionTest { @Test public void functionTest() {test(6, (x) -> System.out.println("接口中的抽象run方法 , 接收参数是:" + x)); } public void test(int n, MyFunction function) { System.out.println(n); function.defaultMethod(n); function.run(n); }}输出结果:6接口中的默认方法 , 接收参数是:6接口中的抽象run方法 , 接收参数是:6
  • 为大家演示下自定义有泛型的函数式接口测试实例:/** * 自定义的有泛型函数式接口 */@FunctionalInterfacepublic interface MyFunctionGeneric { /** * 转换值 * @param t * @return */ T convertValue(T t);}/*** 测试类*/public class MyFunctionGenericTest { @Test public void convertValueTest() { String result = toLowerCase((x) -> x.toLowerCase(), "ABC"); System.out.println(result); } public String toLowerCase(MyFunctionGeneric functionGeneric, String value) {return functionGeneric.convertValue(value); }}输出结果:abc注意:作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递 , 接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口 的类型 。
三、Java8四大内置核心函数式接口首先总览下四大函数式接口的特点说明:
接口参数类型返回类型方法说明ConsumerTvoidvoid accept(T t)消费型接口 , 对类型T参数操作 , 无返回结果Supplier-TT get()供给型接口 , 创造T类型参数FunctionTRR apply(T t)函数型接口 , 对类型T参数操作 , 返回R类型参数PredicateTbooleanboolean test(T t)断言型接口 , 对类型T进行条件筛选操作
消费型接口Consumer
java.util.function.Consumer 接口是消费一个数据 , 其数据类型由泛型决定 。
接口源码:
package java.util.function;import java.util.Objects;@FunctionalInterfacepublic interface Consumer {void accept(T t);default Consumer andThen(Consumer after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}}
  1. 抽象方法:void accept(T t) , 接收并消费一个指定泛型的数据 , 无需返回结果 。