Java8新特性-函数式接口

140 阅读4分钟

简介

  • 什么是函数式接口?

在Java中,是不允许将一个方法作为参数传递给另一个函数,也不允许将一个方法作为值,返回给调用者,这就是所谓的不支持闭包。

  • 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
  • 适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,函数式接口可以被隐式转换为 lambda 表达式。

可以通过使用lambda表达式,来实例化这些接口,以避免让匿名类的实现变得臃肿。但是在编写函数式接口时一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。最简单的例子就是创建Thread线程:

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("线程执行的操作");
    }
}).start();

// lambda写法,因为Runnable中只有一个run方法
new Thread(() -> System.out.println("线程执行的操作")).start();

常用函数式接口

以下列举出常用的几个函数式接口,示例代码

Consumer<T>:消费型接口

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

默认方法:  andThen(Consumer<? super T> after),先执行andThen接口的accept方法,然后在执行andThen方法参数after中的accept方法。

public void testConsumer() {

    Consumer<Integer> action1 = i -> {
        System.out.println(i);
    };

    Consumer<Integer> action2 = i -> {
        System.out.println(i + 3);
    };

    // 先执行action1的消费,再执行action2的消费
    action1.andThen(action2)
            .accept(3);

}

Supplier<T>: 供给型接口

抽象方法:T get(),无参数,有返回值,这类接口适合提供数据的场景。例如:

Supplier<Integer> supplier = () -> {
    Random random = new Random();
    return random.nextInt(3);
};

System.out.println(supplier.get());

Function<T,R>: 函数型接口

抽象方法:  R apply(T t),传入一个参数,进行相应操作,返回想要的结果。

默认方法: compose(Function<? super V, ? extends T> before),先执行compose方法参数before中的apply方法,然后将执行结果传递给调用compose函数中的apply方法在执行。

默认方法: andThen(Function<? super R, ? extends V> after),先执行调用andThen函数的apply方法,然后在将执行结果传递给andThen方法after参数中的apply方法在执行。它和compose方法整好是相反的执行顺序。

Function<Student, Integer> func1 = (student) -> student.getAge();

Function<Integer, Integer> func2 = (age) -> age + 1;

Student student = Student.builder().name("daq").age(23).grade(99).build();

// 先执行compose方法参数before中的apply方法,然后将执行结果传递给调用compose函数中的apply方法在执行
// 如下:先执行func1,在把返回结果当做func2的入参执行。
System.out.println(func2.compose(func1).apply(student));

// 如下:先执行func1,在把返回结果当做func2的入参执行。
System.out.println(func1.andThen(func2).apply(student));

Predicate<T> : 断言型接口

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

默认方法:

  • and(Predicate<? super T> other),相当于逻辑运算符中的&&,当两个Predicate函数的返回结果都为true时才返回true。
  • or(Predicate<? super T> other) ,相当于逻辑运算符中的||,当两个Predicate函数的返回结果有一个为true则返回true,否则返回false。
  • negate(),这个方法的意思就是取反。
Student stu = Student.builder().name("daq").age(23).grade(99).build();

Predicate<Student> predicate1 = (student) -> student.getAge() > 23;
Predicate<Student> predicate2 = (student) -> student.getGrade() > 90;

System.out.println(predicate1.test(stu));
System.out.println(predicate1.and(predicate2).test(stu));
System.out.println(predicate1.or(predicate2).test(stu));
System.out.println(predicate1.negate());

应用场景: 主要是应用在过滤中,如果需要对多个对象进行过滤并执行相同的处理逻辑,那么可以将这些相同的操作封装到 filter 方法中,由调用者提供过滤条件,以便重复使用。

public void predicateFilter() {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    List<String> words = Arrays.asList("a", "ab", "abc");

    filter(numbers, x -> (int)x % 2 == 0);
    filter(words, x -> ((String)x).length() > 1);
}

public static void filter(List list, Predicate condition) {
    list.forEach(x -> {
        if (condition.test(x)) {
            System.out.println("这里是对应的业务逻辑");
        }
    });
}

自定义函数式接口

@FunctionalInterface

这是Java 8中专门为函数式接口引入了一个新的注解,该注解可用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。但是这个注解不是必须的,只要符合函数式接口的定义即可。但最好还是加上,防止他人修改,一看到这个注解就知道是函数式接口,避免他人往接口内添加抽象方法造成不必要的麻烦。

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

那么怎么使用这个自定义的函数式接口呢?我们可以用函数式接口作为参数,调用时传递Lambda表达式。如果一个方法的参数是Lambda,那么这个参数的类型一定是函数式接口。例如:

@FunctionalInterface
public interface MyFunction {
    void show(Student student);
}
public static void main(String[] args) {
    TestFunction testFunction = new TestFunction();
    Student student = Student.builder().name("daiaoqi").age(23).build();
    testFunction.caustomFunc(student, (s) -> System.out.println("这是自定义的函数式接口实现逻辑"));
}

public void caustomFunc(Student s, MyFunction myFunction) {
    myFunction.show(s);
}

如果哪天需求变了,在调用时,换一种逻辑传入即可。