美文网首页
Java高阶函数

Java高阶函数

作者: 程序员汪汪 | 来源:发表于2021-03-17 22:18 被阅读0次

    在阅读本文之前,建议先阅读Lambda表达式(不读也可以),如果你已经熟练使用Lambda表达式,那么请直接阅读本文。本文的代码也提供了多种写法:普通写法 -> Lambda表达式 。能够使用方法引用,也会使用方法引用。

    高阶函数

    高阶函数(higher-order Function):一个消费函数或者产生函数的函数;简而言之,就是一个函数的参数是函数,或者返回值是函数,那么这个函数就是高阶函数。

    那么,什么样的参数或者返回值类型是函数类型的呢?如果该参数或者返回值类型是一个函数式接口,那么这个参数或者返回值就是函数类型的。

    下面来看一个例子,如何生产一个函数(返回值是函数式接口):

    public class ProduceFunction {
    
        Function<String, String> produce() {
            //普通写法
    //        return new Function<String, String>() {
    //            @Override
    //            public String apply(String s) {
    //                return s.toLowerCase();
    //            }
    //        };
            // Lambda表达式
    //        return s -> s.toLowerCase();
            
            //方法引用
            return String::toLowerCase;
        }
    
        public static void main(String[] args) {
    
            ProduceFunction pf = new ProduceFunction();
            Function<String, String> f = pf.produce();
            System.out.println(f.apply("KEKE"));
        }
    }
    /**
    输出结果:
        keke
    */
    

    在这个例子中produce()就是高阶函数。有人会问,这不是个方法吗?没错,它就是个方法,函数式编程是Java借鉴面向函数式编程语言产生的一种新的适合Java的编程方式(不是Java特有),在其他语言中,方法也叫做函数,比如C语言,所以在函数式编程中,我们把方法叫做函数,只是这个方法的返回值类型或者参数类型是一个函数式接口。

    现在再来看如何消费一个函数,消费函数需要在参数列表正确地描述函数类型 ,示例:

    class One {}
    
    class Two {}
    
    public class ConsumeFunction {
        static Two consume(Function<One, Two> function) {
            return function.apply(new One());
        }
    
        public static void main(String[] args) {
            //普通写法
            Two two = consume(new Function<One, Two>() {
                @Override
                public Two apply(One one) {
                    return new Two();
                }
            });
            // Lambda表达式
            Two two1 = consume(one -> new Two());
        }
    }
    

    再来看看基于消费函数生成新函数的例子,代码示例:

    class I {
        @Override
        public String toString() {
            return "I";
        }
    }
    
    class O {
        @Override
        public String toString() {
            return "O";
        }
    }
    
    public class TransformFunction {
        static Function<I, O> transform(Function<I, O> in) {
            //普通写法 这里没有写错,就是Function<O, O>
    //        return in.andThen(new Function<O, O>() {
    //            @Override
    //            public O apply(O o) {
    //                System.out.println(o);
    //                return o;
    //            }
    //        });
            //Lambda表达式
            return in.andThen(o -> {
                System.out.println(o);
                return o;
            });
        }
    
        public static void main(String[] args) {
            //普通写法
            Function<I, O> f = transform(new Function<I, O>() {
                @Override
                public O apply(I i) {
                    System.out.println(i);
                    return new O();
                }
            });
            O o = f.apply(new I());
            // Lambda表达式
            Function<I, O> f2 = transform(i -> {
                System.out.println(i);
                return new O();
            });
            O o2 = f2.apply(new I());
        }
    }
    /**
    运行结果:
        I
        O
        I
        O
    */
    

    上面的两个例子都通俗易懂,这个例子,看着可能就有点懵逼了,不急,先来看看Function这个函数式接口的源码:

    /**
     * 代表这一个方法,能够接受参数,并且返回一个结果
     * @since 1.8
     */
    @FunctionalInterface
    public interface Function<T, R> {
        /**
         * 将参数赋予给相应方法
         * 
         * @param t
         * @return
         */
        R apply(T t);
    
        /**
         * 先执行参数(即也是一个Function)的,再执行调用者(同样是一个Function)
         */
        default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }
        /**
         * 先执行调用者,再执行参数,和compose相反。
         */
        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;
        }
    }
    

    以上是Function这个接口的具体代码,并简要介绍了四个方法的功能。

    这么看还是太抽象了,现在来看一个具体例子:

    public class Test {
        public static void main(String[] args) {
            //普通写法
    //        Function<Integer, Integer> f1 = new Function<Integer, Integer>() {
    //            @Override
    //            public Integer apply(Integer i) {
    //                return i + 1;
    //            }
    //        };
            // Lambda表达式
            Function<Integer, Integer> f1 = i -> i + 1;
            Function<Integer, Integer> g1 = i -> i * 2;
    
            System.out.println(f1.apply(2));    // ---- ①
            System.out.println(g1.apply(2));    // ---- ②
    
            System.out.println(f1.andThen(g1).apply(3));    // ---- ③
            System.out.println(f1.compose(g1).apply(3));    // ---- ④
    
            System.out.println(Function.identity().compose(g1).apply(4));    // ---- ⑤
        }
    /**
    输出结果:
        3
        4
        8
        7
        8
    */
    }
    
    

    输出①、②比较容易理解,就是简单的把参数赋值到apply()里,经过运算返回结果。

    输出③中调用了andThen()f1.andThen(g1).apply(3)的执行顺序就是先执行调用者f1,然后再执行参数g1。这样不是很直观,那么我们可以把它看成是这样的形式:g1.apply(f1.apply(3))这个不就是数学中的函数g(f(x))吗,所以优先执行内部的f(x)也就是f1.apply(3),然后将执行结果作为参数继续执行g(x)也就是g1.apply()

    输出④中调用了compose(),它的执行顺序刚好和andThen()相反,f1.compose(g1).apply(3)的执行顺序是先执行参数g1,然后再执行调用者f1。那么f1.compose(g1).apply(3)就可以看成这样的形式:f1.apply(g1.apply(3));也就可以看成数学函数f(g(x)),这时先执行g(x)也就是g1.apply(3),然后将结果作为参数继续执行f(x)也就是f1.apply()

    输出⑤中的identity()是一个静态方法,它用于返回正在执行的方法,所以Function.identity().compose(g1).apply(4)就相当于g1.apply(4)

    有了这些背景知识,我们回过头再来看看基于消费函数生成新函数的例子:f.apply(new I());。这个代码实际上可以写成:

    // f就是.apply(new I())前面的一大串东西。
    new Function<I, O>() {
                @Override
                public O apply(I i) {
                    System.out.println(i);
                    return new O();
                }
            }.andThen(new Function<O, O>() {
                @Override
                public O apply(O o) {
                    System.out.println(o);
                    return o;
                }
            }).apply(new I());
    

    可以看到这里调用了andThen(),那么根据上面的执行顺序,应该先执行调用者,也就是f1.apply(new I()),返回值为O类型,那么将返回值再次作为参数传入andThen()中的apply()中,那么就得到了最终的输出结果IO

    相关文章

      网友评论

          本文标题:Java高阶函数

          本文链接:https://www.haomeiwen.com/subject/pkrycltx.html