函数式接口
什么是函数式接口
函数式接口(Functional Interface)就是有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
 函数式接口可以被隐式转换为 Lambda 表达式。
 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
定义一个函数式接口
package demo1;
@FunctionalInterface
public interface MyFunInterface {
    public abstract void show();
    
    default void showInit(){}
}
注意:
自定义函数式接口时,@FunctionalInterface是可选的,就算不写这个注解,只要满足函数式接口定义的条件,那么定义的接口依旧是函数式接口。
但是,建议加上该注解。
如何理解函数式接口
Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即Java不但可以支持OOP还可以支持OOF(面向函数编程)
 在函数式编程语言当中,函数被当做一等公民对待。
 在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。 在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
JDK 1.8 之前已有的函数式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口:
java.util.function
Java 内置四大核心函数式接口
JAVA8在java.util.function定义了大量的函数式接口,核心函数式接口主要有一下四个:
| 函数式接口 | 参数类型 | 返回类型 | 用途 | 
|---|---|---|---|
| Interface Supplier | 无 | T | 返回类型为T的对象,包含方法 T get() | 
| Interface Consumer | T | void | 对类型为T的对象应用操作,包含方法: void accept(T t) | 
| Interface Predicate | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法: boolean test(T t) | 
| Interface Function<T,R> | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t) | 
Supplier接口
Interface Supplier:包含了一个无参的方法
- T get():获取结果。
T 是Supplier接口的泛型参数,表示返回结果类型
- 该方法无需要参数,它会按照某种逻辑(由Lambda表达式实现)返回一个结果
- Supplier接口也被称为生产接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用,但不传入参数。
声明如下:
@FunctionalInterface
public interface Supplier<T> {
    T get();
}
T 是Supplier接口的泛型参数,表示返回结果类型
案例1
import java.util.function.Supplier;
/**
 * 理解输出Supplier函数式接口
 * 以返回一个字符串“123”为例
 *
 * @author Anna.
 * @date 2024/4/3 12:37
 */
public class SupplierDemo3 {
    public static void main(String[] args) {
        // 通过实现Supplier接口调用get()方法实现
        Supplier<String> stringSupplier = new Supplier<String>() {
            @Override
            public String get() {
                return "123";
            }
        };
        System.out.printf("实现Supplier接口的Supplier:%s%n", stringSupplier);
        System.out.printf("通过实现Supplier接口调用get()方法实现:%s%n", stringSupplier.get());
        // 通过函数式接口传入Lambda表达式的方式实现
        String str = getStringBySupplier(() -> "123");
        System.out.printf("通过函数式接口传入Lambda表达式的方式实现:%s%n", str);
    }
    /**
     * 定义一个返回类型是String的Supplier接口函数调用方法
     *
     * @author Anna.
     * @date 2024/4/3 12:39
     */
    public static String getStringBySupplier(Supplier<String> supplier) {
        System.out.printf("定义方法中的Supplier:%s%n", supplier);
        return supplier.get();
    }
}
执行结果

理解:
函数式接口关心入参及返回值,执行过程可以看着将Lambda表达作为参数进行传递,具体实现有由Lambda表达式内部方法体完成。
 通过上述结果我们可以看出,实现Supplier接口的Supplier是SupplierDemo3实例的一个内部类,而定义方法中的Supplier返回的却是一个Lambda。
案例2
package demo2;
import java.util.function.Supplier;
/**
 *  使用Supplier<T>定义一个泛型方法,等到一个同类型的返回数据
 *
 * @author Anna.
 * @date 2024/4/1 23:57
 */
public class SupplierDemo {
    public static void main(String[] args) {
        // 匿名内部类方法实现
        String str = null;
        str = get(new Supplier<String>() {
            @Override
            public String get() {
                return "Hello".toUpperCase();
            }
        });
        System.out.printf("匿名内部类方法实现:%s%n",str);
        // Lambda表达式实现
        str = get(() -> {return "Hello".toUpperCase();});
        System.out.printf("Lambda表达式实现:%s%n",str);
        // Lambda表达式简写实现
        str = get(() -> "Hello".toUpperCase());
        System.out.printf("Lambda表达式简写实现:%s%n",str);
        // 方法引用实现
        str = get("Hello"::toUpperCase);
        System.out.printf("方法引用实现:%s%n",str);
    }
    /**
     * 定义一个泛型方法,等到一个同类型的返回数据
     *
     * @param supplier
     * @return T
     * @author Anna.
     * @date 2024/4/2 0:00
     */
    public static <T> T get(Supplier<T> supplier){
        return supplier.get();
    }
}
执行结果

案例3
package demo2;
import java.util.function.Supplier;
/**
 * 使用Supplier<T>定义一个泛型方法,获取数组中的最大值
 *
 * @author Anna.
 * @date 2024/4/1 23:57
 */
public class SupplierDemo2 {
    public static void main(String[] args) {
        // 定义一个数组
        int[] arr = {123, 12, 123, 233, 1231};
        Integer max = get(() -> {
            int rtn = 0;
            for (int item : arr) {
                if (item > rtn) {
                    rtn = item;
                }
            }
            return rtn;
        });
        System.out.println("max = " + max);
        // 抽取方法
        max = get(() -> getInteger(arr));
        System.out.println("max = " + max);
    }
    /**
     * 定义一个泛型方法,等到一个同类型的返回数据
     *
     * @param supplier
     * @return T
     * @author Anna.
     * @date 2024/4/2 0:00
     */
    public static <T> T get(Supplier<T> supplier) {
        return supplier.get();
    }
    /**
     * 提取成一个方法
     *
     * @param arr
     * @return java.lang.Integer
     * @author Anna.
     * @date 2024/4/2 9:22
     */
    private static Integer getInteger(int[] arr) {
        int rtn = 0;
        if (arr != null && arr.length > 0) {
            for (int item : arr) {
                if (item > rtn) {
                    rtn = item;
                }
            }
        }
        return rtn;
    }
}
执行结果

Consumer接口
Interface Consumer:包含两个方法:
- void accept(T t):对给定的参数执行操作
T 是Consumer接口的泛型参数,表示返回传入参数类型
- default Consumer andThen(Consumer<? super T> after):Consumer组合依次执行andThen操作,然后执行after操作。
 Consumer接口也被称为消费型接口,它消费的数据类型由泛型指定,不返回任何结果。
案例1
package demo3;
import java.util.function.Consumer;
/**
 * String[] strArray = {"张三,数学,30","李四,语文,40","王五,体育,100"};
 * 字符串数组中包含多条信息,请按照格式,“姓名:XXX,科目:XXX,分数:XXX”的格式将信息打印出来
 * 要求:
 * 把打印姓名的动作作为第一个Consumer接口的Lambda示例
 * 把打印科目的动作作为第二个Consumer接口的Lambda示例
 * 把打印分数的动作作为第三个Consumer接口的Lambda示例
 * 将三个Consumer接口按照顺序组合到一起使用
 *
 * @author Anna.
 * @date 2024/4/3 12:08
 */
public class ConsumerDemo {
    public static void main(String[] args) {
        String[] strArray = {"张三,数学,30", "李四,语文,40", "王五,体育,100"};
        print(strArray,
                s -> System.out.print("姓名:" + s.split(",")[0] + ","),   // 第一个Consumer接口的Lambda
                s -> System.out.print("科目:" + s.split(",")[1] + ","),   // 第二个Consumer接口的Lambda
                s -> System.out.println("分数:" + s.split(",")[2]));     // 第三个Consumer接口的Lambda
    }
    public static void print(String[] arr, Consumer<String> com1, Consumer<String> com2, Consumer<String> com3) {
        for (String str : arr) {
            // 链式调用 等价于
            //  com1.accept(str);
            //  com2.accept(str);
            //  com3.accept(str);
            com1.andThen(com2).andThen(com3).accept(str);
        }
    }
}
执行结果

案例2
package demo3;
import java.util.function.Consumer;
/**
 * 定义一个Config对象,通过函数式接口,完成数据的初始化
 *
 * @author Anna.
 * @date 2024/4/3 12:08
 */
public class ConsumerConfig {
    private String ip;
    private Integer port;
    /**
     * 初始化对象
     *
     * @param consumer
     * @return void
     * @author Anna.
     * @date 2024/4/3 12:25
     */
    public void init(Consumer<ConsumerConfig> consumer) {
        consumer.accept(this);
    }
    public void setIp(String ip) {
        this.ip = ip;
    }
    public void setPort(Integer port) {
        this.port = port;
    }
    @Override
    public String toString() {
        return "ConsumerConfig{" +
                "ip='" + ip + '\'' +
                ", port=" + port +
                '}';
    }
    public static void main(String[] args) {
        ConsumerConfig config = new ConsumerConfig();
        System.out.println("未初始化之前:" + config);
        config.init((consumer) -> {
            consumer.setIp("127.0.0.1");
            consumer.setPort(8080);
        });
        System.out.println("初始化之后:" + config);
    }
}
执行结果

Predicate接口
Interface Predicate:常用的四个方法:
- boolean test(T t):对给定的参数进行判断(判断逻辑有Lambda表达式实现),返回一个布尔值
T 是Predicate接口的泛型参数,表示返回传入参数类型
- default Predicate and(Predicate<? super T> other):返回一个逻辑的否定,对应逻辑非(!),源码如下
default Predicate<T> negate() {return (t) -> !test(t);}
- default Predicate negate():返回一个组合判断,对应短路与(&&),源码如下
default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
}
- default Predicate or(Predicate<? super T> other):返回一个组合判断,对应逻辑或(||),源码如下
default Predicate<T> or(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
}
案例1
package demo4;
import java.util.function.Predicate;
/**
 * 使用函数式接口Predicate,完成以下校验
 * 1 判断一个字符串长度是否大于5
 * 2 判断一个字符串长度小于5且已S开头
 * 3 判断一个字符串小于等于5
 *
 * @author Anna.
 * @date 2024/4/3 14:10
 */
public class PredicateDemo {
    public static void main(String[] args) {
        // 判断一个字符串长度是否大于5
        test01("12321", s -> s != null && s.length() > 5);
        // 判断一个字符串长度小于5且已S开头
        test02(null, s -> s != null && s.length() > 5, s -> s.startsWith("S"));
        // 判断一个字符串小于等于5
        test03("S2321", s -> s != null && s.length() > 5);
    }
    public static void test01(String str, Predicate<String> predicate) {
        System.out.println(predicate.test(str));
    }
    public static void test02(String str, Predicate<String> pdc1, Predicate<String> pdc2) {
        System.out.println(pdc1.and(pdc2).test(str));
    }
    public static void test03(String str, Predicate<String> pdc1) {
        System.out.println(pdc1.negate().test(str));
    }
}
执行结果

Function接口
Interface Function<T,R>:常用的两个方法:
- R apply(T t):将此函数应用于给定的参数
T,R 是Function接口的泛型参数,T表示返回传入参数类型, R表示返回参数类型
- default Function<T,V> andThen(Function<? super R,? extends V> after):返回一个组合函数,首先将该函数应用于输出,然后将after函数应用于结果。源码如下:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}
- Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),并返回一个新的值。
案例1
package demo5;
import java.util.function.Function;
/**
 * 使用Function函数式接口:
 *      1 将一个字符串数字,转换成int类型,输出在控制台
 *      2 将一个字符串数字,转换成int类型,然后使用第二个Lambda表达式完成,加20,最后输出在控制台
 * @author Anna.
 * @date 2024/4/3 14:59
 */
public class FunctionDemo {
    public static void main(String[] args) {
        // 将一个字符串数字,转换成int类型,输出在控制台
        print("213", Integer::parseInt);
        // 将一个字符串数字,转换成int类型,然后使用第二个Lambda表达式完成,加20,最后输出在控制台
        printAdd("100",Integer::parseInt,s -> s + 20);
    }
    public static void print(String str, Function<String,Integer> func){
        System.out.println(func.apply(str));
    }
    public static void printAdd(String str, Function<String,Integer> func1,Function<Integer,Integer> func2){
        System.out.println(func1.andThen(func2).apply(str));
    }
}
执行结果

gitee源码
git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git

![MapReduce [OSDI‘04] 论文阅读笔记](https://img-blog.csdnimg.cn/direct/5ce9c9cff2de4ad889f25585f1ac910c.png#pic_center)















![[RK3588-Android12] 调试MIPI-双通道-压缩屏(Video Mode/MIPI Dphy 8Lane/DSC 144HZ)](https://img-blog.csdnimg.cn/direct/2ac86bc95b754720a4357ee834fd793c.png)

