Java高阶函数

2021-03-17  本文已影响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

上一篇 下一篇

猜你喜欢

热点阅读