什么是函数式接口和常用的函数式接口

什么是函数式接口和常用的函数式接口

1. 什么是函数式接口

接口中有且只有一个 抽象方法的接口被称作函数式接口

Java 中函数式编程体现就是 Lambda 表达式,所以对于 Lambda 表达式来说函数式接口尤为重要

如何检测一个接口是不是函数式接口?

@FunctionalInterface 注解,在定义接口的上方加上注解,编译可以通过就是函数式接口,否则不是

【注意】
在自己写接口时,如果接口中只含有一个 abstract 方法那么这样的接口无论加不加 @FunctionalInterface 注解都是函数式接口,科学建议这样的接口一定要加上@FunctionalInterface 注解

2. 常用的函数式接口

2.1 Consumer 接口 ( 消费者接口 )

Consumer 接口又被称作消费者接口
接口中有一个 void accept( T t ); 方法,方法需要一个泛型参数,但是没有返回值

Consumer 接口源码:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

代码演示:

定义了一个测试方法 testConsumerLambda () 需要参数为:a. 一个 String 类型字符串 b. Consumer 接口,接口直接约束参数泛型为 String 类型,方法体为接口的实现类对象调用 accept () 方法。

main 方法中 a. 使用 Lambda 表达是重写了 accept () 方法

​ b. 方法引用,引用 System 中的 out ( ) 方法 作为 accept () 的重写方法,为什么可以引用? 前提必须要求 引用的方法的参数列表和返回值必须和函数式接口中 abstract 修饰的方法的 参数列表和返回值相同。

package com.zixue.demo;

import java.util.function.Consumer;

/**
 * @author: 85409 2023/3/7 20:42
 * @description: TODO
 */
public class ConsumerDemo {
    public static void main(String[] args) {
        // Lambda 表达式
        testConsumerLambda("Lambda表达式", s -> System.out.println(s));

        // 方法引用
        testConsumerLambda("Lambda表达式", System.out::println);
    }

    public static void testConsumerLambda(String s, Consumer<String> con) {
        con.accept(s);
    }
}

2.2 Supplier 接口 ( 生产者接口 )

Supplier 接口又称作生产者接口
接口中有一个 R get(); 方法,方法不需要参数,但是有一个返回值

Supplier 接口源码:

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

代码演示:

package com.zixue.demo;

import java.util.function.Supplier;

/**
 * @author: 85409 2023/3/7 20:51
 * @description: TODO
 */
public class SupplierDemo {
    public static void main(String[] args) {
        // Lambda 表达式
        testLambda(() -> "我是 Supplier 返回的字符串");
    }

    public static void testLambda(Supplier<String> sup) {
        String s = sup.get();
        System.out.println(s);
    }
}

2.3 Predicate 接口 ( 过滤,判断接口 )

Predicate 接口又称作过滤,判断型接口
接口中有一个 boolean test( T t ) 方法,需要一个泛型参数,返回值为 boolean 类型

Predicate 接口 源码:

public interface Predicate<T> {
    boolean test(T t);
}

代码演示

public class PredicateDemo {
    public static void main(String[] args) {
        // Lambda 表达式
        String str = "我肯定大于5吧";
        testLambda(str, s -> str.length() > 5);
    }

    public static void testLambda(String s, Predicate<String> pre) {
        boolean test = pre.test(s);
        if (test) {
            System.out.println("字符串长度大于5");
        }else {
            System.out.println("字符串长度不大于5");
        }
    }
}

stream 流中 filter 方法的参数也为 Predicate 接口,可以使用 Lambda 方式实现方法。
Predicate 接口中 boolean test(T t) 返回值为 boolean 类型,filter 会自动过滤到 false 结果,来实现集合中元素的过滤。

package com.zixue.demo;

import java.util.ArrayList;

/**
 * @author: 85409 2023/3/7 21:34
 * @description: TODO
 */
public class PredicateStreamDemo {
    public static void main(String[] args) {
        // stream 流
        ArrayList<String> list = new ArrayList<>();
        list.add("汤米·谢尔比");
        list.add("亚瑟·谢尔比");
        list.add("约翰·谢尔比");
        list.add("所罗门");
        list.add("Ada Shelby");
        list.add("Grace Burgess");
        list.add("Alfie Solomons");

        // filter 参数为 Predicate 接口 进而调用  boolean test(T t) 方法,来实现过滤的目的
        list.stream()
                .filter(s -> s.length() > 6)
                .forEach(System.out::println);
    }
}

2.4 Comparator 接口( 比较器接口 )

Comparator 接口又称作比较器接口
接口中有一个 int compare( T o1, T o2 ); 方法,方法需要两个参数,有一个 int 类型返回值

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
}

代码演示:

代码以 Person 类型数组作为例子 Person 类中 成员属性为 int 类型 id ,String 类型 name,int 类型 age

定义方法 testLambda() 方法 需要传入参数 Person 类型数组 和 Compartor 比较器接口,方法体中使用 Arrays 工具类中的 sort() 方法 需要参数为: 需要排序的数组,排序的方式

需要排序的数组:Person 类型数组

排序的方式: 我们传入 Compartor 接口自定义排序方式。代码中重写的 compare 方法是,通过 Person 中 age 属性进行排序

/*
Person 类
*/
class Person {
    private int id;
    private String name;
    private int age;
    
    ...... // 省略了 getter and setter 方法 和 构造方法 toString 方法
}


public class CompartorDemo {
    public static void main(String[] args) {
        Person[] arr = new Person[10];
        for (int i = 0; i < 10; i++) {
            arr[i] = new Person(i + 1, "张三", (int) (Math.random() * 100));
        }
        // Lambda 表达式
        testLambda(arr, (p1, p2) -> p1.getAge() - p2.getAge());
        // stream 流 + 方法引用
        Arrays.stream(arr).forEach(System.out::println);
    }

    public static void testLambda(Person[] arr, Comparator<Person> com) {
        // 使用了 Arrays 工具类的 sort() 排序方法
        Arrays.sort(arr, com);
    }
}

2.5 Function<T,R> 接口 (类型转换接口)

Function 接口又被称为转换接口
接口中有一个 R apply ( T t ); 泛型 T 通过传入参数时约束具体数据类型, 返回值类型是自定义方法时程序员想要的被返回的数据类型。

@FunctionalInterface
public interface Function<T> {
    R apply(T t);
}

代码演示:

定义了一个测试方法,方法需要传入两个参数

​ a. char 类型数组,用于数据类型转换的参数,目的将 char 类型数组转换为 String 类型
​ b. Function 接口,使用接口中 唯一 abstract 方法 apply ( ) 达到转换类型的目的

最后在 main 方法中将 chars 数组作为参数传入 testLambda 方法中,使用 Lambda 表达式对 apply ( ) 方法进行重写。

package com.zixue.demo;

import java.util.function.Function;

/**
 * @author: 85409 2023/3/7 22:53
 * @description: TODO
 */
public class FunctionDemo {
    public static void main(String[] args) {
        char[] chars = {'L', 'a', 'm', 'b', 'd', 'a', '表', '达', '式', '演', '示', 'F', 'u', 'c', 't', 'i', 'o', 'n', '接', '口'};
        testLambda(chars, (s) -> new String(chars));
    }

    /**
     * 测试方法使用 Lambda 表达式实现函数式接口
     *
     * @param chars 传入的 char[] 类型数组
     * @param fun 参数为 Function 接口
     */
    public static void testLambda(char[] chars, Function<char[], String> fun) {
        System.out.println(fun.apply(chars));
    }
}