1.函数式接口是什么?

有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使用Lambda表达式创建一个函数式接口的对象,一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。
@FunctionalInterface注解
与@Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface 。该注解可用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。但是这个注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。
static方法和default方法
实在不知道该在哪介绍这两个方法了,所以就穿插在这里了。
static方法:
java8中为接口新增了一项功能,定义一个或者多个静态方法。用法和普通的static方法一样,例如:

  1. public interface Interface {
  2. /** * 静态方法 */
  3. static void staticMethod() {
  4. System.out.println("static method");
  5. }
  6. }

注意:实现接口的类或者子接口不会继承接口中的静态方法。
default方法:
java8在接口中新增default方法,是为了在现有的类库中中新增功能而不影响他们的实现类,试想一下,如果不增加默认实现的话,接口的所有实现类都要实现一遍这个方法,这会出现兼容性问题,如果定义了默认实现的话,那么实现类直接调用就可以了,并不需要实现这个方法。default方法怎么定义?

  1. public interface Interface {
  2. /** * default方法 */
  3. default void print() {
  4. System.out.println("hello default");
  5. }
  6. }

注意:如果接口中的默认方法不能满足某个实现类需要,那么实现类可以覆盖默认方法。不用加default关键字,例如:

  1. public class InterfaceImpl implements Interface {
  2. @Override
  3. public void print() {
  4. System.out.println("hello default 2");
  5. }
  6. }

在函数式接口的定义中是只允许有一个抽象方法,但是可以有多个static方法和default方法。


2. 自定义函数式接口

按照下面的格式定义,你也能写出函数式接口:

  1. @FunctionalInterface 修饰符 interface 接口名称 {
  2. 返回值类型 方法名称(可选参数信息);
  3. // 其他非抽象方法内容
  4. }

虽然@FunctionalInterface注解不是必须的,但是自定义函数式接口最好还是都加上,一是养成良好的编程习惯,二是防止他人修改,一看到这个注解就知道是函数式接口,避免他人往接口内添加抽象方法造成不必要的麻烦。

  1. @FunctionalInterface public interface MyFunction {
  2. void print(String s);
  3. }

看上图是我自定义的一个函数式接口,那么这个接口的作用是什么呢?就是输出一串字符串,属于消费型接口,是模仿Consumer接口写的,只不过这个没有使用泛型,而是将参数具体类型化了,不知道Consumer没关系,下面会介绍到,其实java8中提供了很多常用的函数式接口,Consumer就是其中之一,一般情况下都不需要自己定义,直接使用就好了。那么怎么使用这个自定义的函数式接口呢?我们可以用函数式接口作为参数,调用时传递Lambda表达式。如果一个方法的参数是Lambda,那么这个参数的类型一定是函数式接口。例如:

  1. public class MyFunctionTest {
  2. public static void main(String[] args) {
  3. String text = "试试自定义函数好使不";
  4. printString(text, System.out::print);
  5. }
  6. private static void printString(String text, MyFunction myFunction) {
  7. myFunction.print(text);
  8. }
  9. }

执行以后就会输出“试试自定义函数好使不”这句话,如果某天需求变了,我不想输出这句话了,想输出别的,那么直接替换text就好了。函数式编程是没有副作用的,最大的好处就是函数的内部是无状态的,既输入确定输出就确定。函数式编程还有更多好玩的套路,这就需要靠大家自己探索了。

3. 常用函数式接口

3.1 Consumer:消费型接口

抽象方法: void accept(T t),接收一个参数进行消费,但无需返回结果。
使用方式:

  1. Consumer consumer = System.out::println;
  2. consumer.accept("hello function");

默认方法: andThen(Consumerafter),先消费然后在消费,先执行调用andThen接口的accept方法,然后在执行andThen方法参数after中的accept方法。
使用方式:

  1. Consumer<String> consumer1 = s -> System.out.print("车名:"+s.split(",")[0]);
  2. Consumer<String> consumer2 = s -> System.out.println("-->颜色:"+s.split(",")[1]);
  3. String[] strings = {"保时捷,白色", "法拉利,红色"};
  4. for (String string : strings) {
  5. consumer1.andThen(consumer2).accept(string);
  6. }

输出:

  1. 车名:保时捷-->颜色:白色车名:法拉利-->颜色:红色

java8 Stream中使用:

  1. void forEach(Consumer<? super T> action);

例如,控制台输出:

  1. Arrays.asList(1, 2, 3).stream().forEach(i -> System.out.println(i));

3.2 Supplier: 供给型接口

抽象方法:T get(),无参数,有返回值。
使用方式:

  1. Supplier<String> supplier = () -> "我要变的很有钱";
  2. System.out.println(supplier.get());

最后输出就是“我要变得很有钱”,这类接口适合提供数据的场景。
java8 Stream中使用:

  1. public static<T> Stream<T> generate(Supplier<T> s) {
  2. Objects.requireNonNull(s);
  3. return StreamSupport.stream(
  4. new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
  5. }

例如,生成一个stream:

  1. Stream<Integer> stream = Stream.generate(() -> new Random().nextInt(10));
  2. stream.forEach(e -> System.out.println(e));


3.3 Function: 函数型接口

抽象方法: R apply(T t),传入一个参数,返回想要的结果。
使用方式:

  1. Function<Integer, Integer> function1 = e -> e * 6;
  2. System.out.println(function1.apply(2));

很简单的一个乘法例子,显然最后输出是12。
java8 Stream中使用:

  1. <R> Stream<R> map(Function<? super T, ? extends R> mapper);

例如,生成一个新的list:

  1. List list1 = Arrays.asList(1, 2, 3).stream().map(i -> i * i).collect(Collectors.toList());

默认方法:

  • compose(Function before),先执行compose方法参数before中的apply方法,然后将执行结果传递给调用compose函数中的apply方法在执行。
    使用方式:

    1. Function<Integer, Integer> function1 = e -> e * 2;
    2. Function<Integer, Integer> function2 = e -> e * e;
    3. Integer apply2 = function1.compose(function2).apply(3);
    4. System.out.println(apply2);

    还是举一个乘法的例子,compose方法执行流程是先执行function2的表达式也就是3_3=9,然后在将执行结果传给function1的表达式也就是9_2=18,所以最终的结果是18。

  • andThen(Function
    after),先执行调用andThen函数的apply方法,然后在将执行结果传递给andThen方法after参数中的apply方法在执行。它和compose方法整好是相反的执行顺序。
    使用方式:

    1. Function<Integer, Integer> function1 = e -> e * 2;
    2. Function<Integer, Integer> function2 = e -> e * e;
    3. Integer apply3 = function1.andThen(function2).apply(3);
    4. System.out.println(apply3);

    这里我们和compose方法使用一个例子,所以是一模一样的例子,由于方法的不同,执行顺序也就不相同,那么结果是大大不同的。andThen方法是先执行function1表达式,也就是3_2=6,然后在执行function2表达式也就是6_6=36。结果就是36。
    静态方法:identity(),获取一个输入参数和返回结果相同的Function实例。
    使用方式:

    1. Function<Integer, Integer> identity = Function.identity();
    2. Integer apply = identity.apply(3);
    3. System.out.println(apply);

    平常没有遇到过使用这个方法的场景,总之这个方法的作用就是输入什么返回结果就是什么。

3.4 Predicate : 断言型接口

抽象方法: boolean test(T t),传入一个参数,返回一个布尔值。
使用方式:

  1. Predicate<Integer> predicate = t -> t > 0;
  2. boolean test = predicate.test(1);
  3. System.out.println(test);

当predicate函数调用test方法的时候,就会执行拿test方法的参数进行t -> t > 0的条件判断,1肯定是大于0的,最终结果为true。
默认方法:

  • and(Predicate other),相当于逻辑运算符中的&&,当两个Predicate函数的返回结果都为true时才返回true。
    使用方式:

    1. Predicate<String> predicate1 = s -> s.length() > 0;
    2. Predicate<String> predicate2 = Objects::nonNull;
    3. boolean test = predicate1.and(predicate2).test("&&测试");
    4. System.out.println(test);
  • or(Predicate other) ,相当于逻辑运算符中的||,当两个Predicate函数的返回结果有一个为true则返回true,否则返回false。
    使用方式:

    1. Predicate<String> predicate1 = s -> false;
    2. Predicate<String> predicate2 = Objects::nonNull;
    3. boolean test = predicate1.and(predicate2).test("||测试"); System.out.println(test);
  • negate(),这个方法的意思就是取反。
    使用方式:

    1. Predicate<String> predicate = s -> s.length() > 0;
    2. boolean result = predicate.negate().test("取反");
    3. System.out.println(result);

    很明显正常执行test方法的话应该为true,但是调用negate方法后就返回为false了。
    静态方法:isEqual(Object targetRef),对当前操作进行”=”操作,即取等操作,可以理解为 A == B。
    使用方式:

    1. boolean test1 = Predicate.isEqual("test").test("test");
    2. boolean test2 = Predicate.isEqual("test").test("equal");
    3. System.out.println(test1); //true
    4. System.out.println(test2); //false

    java8 Stream中使用:

    1. Stream<T> filter(Predicate<? super T> predicate);
    2. boolean anyMatch(Predicate<? super T> predicate);

    例如,判断是否存在:

    1. System.out.println("断言示例:"+Arrays.asList(1, 2, 3).stream().anyMatch(i->i==2)); //true


    3.5 代码示例:

    ```java package com.tools.java8;

import java.text.SimpleDateFormat; import java.util.Date; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier;

/**

  • 版权:深圳航天信息有限公司 *
  • @description:
  • @author: zhangqing
  • @date: 2021-11-04 */ public class FunctionInterfaceTest { public static void main(String[] args) {

    1. consumer();
    2. supplier();
    3. function();
    4. functionCompose();
    5. predicate();

    }

    /**

    • 消费性接口的定义为void accept(T t),它只接收一个参数t,但是没有返回值。 */ public static void consumer() { Consumer c = System.out::println; c.accept(“这是个消费型接口”); }

      /**

    • 供应接口的定义为T get(),它不接收任何参数,但却有返回值 */ public static void supplier() { Supplier s = () -> {

      1. SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
      2. String str = format.format(new Date());
      3. return str;

      }; System.out.println(s.get()); Supplier s2 = “Abc”::toLowerCase; System.out.println(s2.get()); }

      /**

    • 功能性接口的接口定义为R apply(T t),它接收一个功能参数t,并返回一个功能结果R。 */ public static void function() { Function f = a -> a.split(“[.]”); String[] s = f.apply(“a.b.c”); System.out.println(s.length ); }

      /**

    • 先执行compose方法参数before中的apply方法,然后将执行结果传递给调用compose函数中的apply方法在执行。 */ public static void functionCompose() { Function f = a -> a.toUpperCase(); Function f2 = a -> a.toLowerCase(); String s = f.compose(f2).apply(“ComPose”); System.out.println(s); }

      /**

    • boolean test(T t),传入一个参数,返回一个布尔值。 */ public static void predicate() { Predicate p = t -> t > 10; System.out.println(p.test(10)); System.out.println(p.test(11)); }

}

```