一、前言

早期的程序员在JDK1.5时期,掌握泛型、枚举、反射 基本上就可以了,随着JDK不断迭代升级,到相对于稳定的JDK1.8版本,扩展了大量的知识点,例如:lambda表达式,链式编程,函数式接口,Stream流式计算  这些新知识点的出现大大的简化了编程模型,减少了很多冗余代码,代码简洁,清晰明了,并且在迭代出来的底层架构大量得到了应用,所以,我们必须随着主流进行学习,避免阅读源码时都不知道其意。

二、函数式接口(Functional Interface)

函数式接口:只有一个方法的接口

有且仅有一个抽象方法的接口,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。Lambda就是Java中函数式编程的体现。

类型

主要分布在 java.util.function 包下,常见的 4大原始函数 接口为:Function (函数型接口)、Predicate (断定型接口)、Consumer (消费型接口)、Supplier (供给型接口)

@FunctionalInterface注解

Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface 。该注解放在接口上,表示此接口是一个函数式接口。并且提示编译器去检查接口是否仅包含一个抽象方法,即,是否符合函数式编程的定义。

注意:如果自定义一个符合规范的函数式接口,也可以不加@FunctionalInterface注解,此注解只是起到一个提示编译器进行规范检查的作用

日常开发中用的最多的函数式接口的,比如线程中的 Runnable

@FunctionalInterface
public interface Runnable {

    public abstract void run();
}

1. Function (函数型接口)

有一个输入参数,有一个输出

  接口的源码如下:

@FunctionalInterface
public interface Function<T, R> {

    // 继承该接口需要实现的方法
    R apply(T t); 

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
   
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

apply ()方法就是该接口的唯一方法,也就是继承该Function接口,唯一需要实现的方法!

有小伙伴可能还会问,函数式接口不是 有且只有一个方法 吗?为什么存在default 和 static的方法

  • static:实现接口的类或子接口不会继承接口里的静态方法!
  • default:java8在接口中新增default方法,是为了在现有的类库中新增功能而不影响其实现类,也就是说,该接口的实现类可以直接调用default方法,而不需要实现它!

我们来具体使用下该接口

// 输出输入的参数:有一个输入参数,和一个输出
public static void main(String[] args) {
        // 1. 初始化,并且实现该接口的唯一实现方法
        Function<String,String> function = new Function<String, String>() {
            @Override
            public String apply(String str) {
                return str;
            }
        };
        System.out.println(function.apply("abc"));
}

执行结果:str是一个参数传递到 Predicate 接口的 apply 方法 。表示接受一个参数并产生结果

这就是一个标准的函数式接口,我们前面也说了:Lambda就是Java中函数式编程的体现  也就是说只要是函数式接口,就可以使用lambda表达式来简化代码!如下:

public static void main(String[] args) {
     // 使用lambda表达式
     Function<String,String> function = (str)->{return str;};
     //或者我们可以更简单点,把str的()括号去掉也是可以的
    // Function<String,String> function = str->{return str;};
     System.out.println(function.apply("abc"));
}

这就是典型的lambda表达式语法:()->{};str是传入的参数,等同于我们线程中的Runnable用法:

// Thread内部是一个Runnable的函数式接口,run()是Runnable接口中唯一的方法
Thread thread = new Thread(new Runnable() {
     @Override
     public void run() {
         System.out.println(1);
     }
});

// 上述代码等同于:
Thread thread2 = new Thread(()->{System.out.println(1);});

2. Predicate (断定型接口)

 有一个输入参数,返回值只能是布尔值

源码如下:

@FunctionalInterface
public interface Predicate<T> {

    // 在给定的参数上评估这个谓词
    boolean test(T t);
}

 具体使用:

public static void main(String[] args) {
         /* Predicate<Integer> predicate = new Predicate<Integer>(){
            @Override
            public boolean test(Integer n) {
                return n>0;
            }
        };*/
        // 使用lambda表达式  判断传入的值是否大于0
        Predicate<Integer> predicate =(n)->{return n>0;};
        System.out.println(predicate.test(2));
}

执行结果:n 是一个参数传递到 Predicate 接口的 test 方法 ,如果 n> 0,则test 方法返回 true

针对断定型接口,我们还可以更简洁的lambda表达式语法:

省略()和{}:要传入的参数->判断结果

public static void main(String[] args) {
    Predicate<Integer> predicate = n-> n>0;
    System.out.println(predicate2.test(2));
}

——可以用来判断字符串是否为空,以及求奇数偶数,例如:

/**
 * 断定型接口:有一个输入参数,返回值只能是布尔值
 */
public class PredicateDemo {
    public static void main(String[] args) {
    

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

        /**  1.
         *  Predicate<Integer> predicate = n -> true
         *  n 是一个参数传递到 Predicate 接口的 test 方法
         *  n 如果存在则 test 方法返回 true
         */
        System.out.println("输出全部:");
        eval(list, n->true);


        /** 2.
         *  Predicate<Integer> predicate1 = n -> n%2 == 0
         *  n 是一个参数传递到 Predicate 接口的 test 方法
         *  如果 n%2 为 0 test 方法返回 true
         */
        System.out.println("输出所有偶数:");
        eval(list, n-> n%2 == 0 );


        /** 3.
         *  Predicate<Integer> predicate1 = n -> n > 3
         *  n 是一个参数传递到 Predicate 接口的 test 方法
         *  如果 n > 3  test 方法返回 true
         */
        System.out.println("输出大于3 的所有数字:");
        eval(list, n-> n > 3 );

    }


    public static void eval(List<Integer> list, Predicate<Integer> predicate) {
        for(Integer n: list) {
            // test验证n的表达式的为true还是false
            if(predicate.test(n)) {
                System.out.print(n + " ");
            }
        }
        System.out.println();
    }
}

执行结果:

3. Consumer (消费型接口)

只有入参,没有返回值

源码如下:

@FunctionalInterface
public interface Consumer<T> {

    // t 是输入参数
    void accept(T t);
}

具体使用:

public static void main(String[] args) {
     // 1.常规方式
     /* Consumer<String> consumer = new Consumer<String>(){
            @Override
            public void accept(String str) {
                System.out.println(str);
            }
     };*/

     // 2.lambda表达式
     // Consumer<String> consumer = (str)-> {System.out.println(str)};
     // 更简洁的lambda表达式,省略()、{}
     Consumer<String> consumer = str-> System.out.println(str);

     consumer.accept("abc");
}

执行结果:消费型接口,即只消费,没有返回值

4. Supplier (供给型接口)

没有参数,只有返回值

源码如下:

@FunctionalInterface
public interface Supplier<T> {

    // 获得结果 T是结果的具体类型
    T get();
}

 具体使用

public static void main(String[] args) {
       // 1.常规方式
       /* Supplier<Integer> consumer = new Supplier<Integer>(){
            @Override
            public Integer get() {
                return 1024;
            }
        };*/

        // 2.lambda表达式
        //Supplier<Integer> supplier = ()-> {return 1024;};
        // 更简洁的lambda表达式,省略{}
        Supplier<Integer> supplier = ()-> 1024;

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

执行结果:供给型接口,顾名思义只提供,即返回结果,类似生产者

总结

函数型编程在Java8中得到大量的运用,比如Java8的新特性Stream流式计算,可参考文章:

 

优秀的判断力来自经验,但经验来自于错误的判断。


版权声明:本文为w15558056319原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/w15558056319/article/details/121006932