Java函数式编程的前生今世

作者: realnineyang | 来源:发表于2020-04-18 15:13 被阅读0次

    随着Java8的发布,大家翘首以待的FP(函数式编程,后文皆以FP简称)终于面世。其实早在1.7这个版本就已经准备发布,但是由于还属于Sun刚被收购的磨合期所耽误。而Java8这个版本也一再延误才终于发布。
    早些时候说起Java,大家对他的第一印象就是冗长,虽然我们可以通过IDEA等工具帮我们解决这些问题,但是可读性差的问题仍无法避免。
    于是,lambda和函数式编程呼之欲出。
    Java中函数是表达式与lambda密不可分,而说到lambda表达式,又就不得不提及@FunctionInterface这个注解,当你点开这个注解,你会发现很多你熟悉的类(Comparator、Runnable等)都使用了这个注解,而这个注解也是Java实现函数式编程编程中尤为重要的一环。

    在解释这个注解实现原理之前,我们不妨先使用一下lambda看看其效果。

    //Java8之前
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    });
    //Java8之后
    Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getName()));
    //或者你可以使用方法引用
    Thread thread1 = new Thread(StreamDemo::sayHello);
    private static void sayHello() {
        System.out.println("hello,world");
    }
    

    原理

    如果是你,你会如何设计这套语法糖的实现呢?
    万变不离其宗,lambda只是语法糖,虽然我们使用lambda表达式简化了我们的代码,但是并不意味着我们不需要实现Runnable,我们不妨大胆猜测一下:是不是因为@FunctionalInterface注解自动帮我们生成了一个Runnable的实现?
    没错,Java会在编译时帮我们动态生成一个对象来实现Runnable,我们可以通过参数-Djdk.internal.lambda.dumpProxyClasses来帮助我们把生成的类输出。

    java -classpath your-class-path -Djdk.internal.lambda.dumpProxyClasses com.nineyang.LambdaDemo
    

    通过执行上述代码,我们会发现生成了两个名字比较奇特的类:


    //LambdaDemo$$Lambda$1.class
    final class LambdaDemo$$Lambda$1 implements Runnable {
        private LambdaDemo$$Lambda$1() {
        }
    
        @Hidden
        public void run() {
            LambdaDemo.lambda$main$0();
        }
    }
    
    //LambdaDemo$$Lambda$2.class
    final class LambdaDemo$$Lambda$2 implements Runnable {
        private LambdaDemo$$Lambda$2() {
        }
    
        @Hidden
        public void run() {
            LambdaDemo.sayHello();
        }
    }
    

    此时又有一个奇奇怪怪的方法出现了,LambdaDemo.sayHello()我们可以理解,但是LambdaDemo.lambda$main$0()是什么鬼?我们可重来没有写过这个方法啊。
    我们不妨再猜测一下,是不是因为生成实例的同时,也给我们生成了一个方法呢?既然这个静态方法在我们所写的类中,那我们不妨看看这个类生成的字节码:

    //命令行执行
    javap -p -c target.classes.com.nineyang.LambdaDemo
    
    //截取其中一部分字节码
    private static void lambda$main$0();
        Code:
           0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: invokestatic  #10                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
           6: invokevirtual #11                 // Method java/lang/Thread.getName:()Ljava/lang/String;
           9: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          12: return
    

    我想看到这里,已经不需要我再过多解释,大家也能明白其所做的事情是什么了。
    不过,也许此时在你心中有了一个疑惑,为什么这些名字这么奇怪?在哪里可以看到生成的规则呢?如果我自定义一个FunctionInterface结果会怎样呢?

    public class LambdaDemo2 {
    
        public static void main(String[] args) {
            LambdaPrintFunction lambdaPrintFunction = System.out::println;
            lambdaPrintFunction.print("hello,nine");
        }
    }
    
    @FunctionalInterface
    interface LambdaPrintFunction {
        void print(String x);
    }
    
    //生成的实现类
    final class LambdaDemo2$$Lambda$1 implements LambdaPrintFunction {
        private final PrintStream arg$1;
    
        private LambdaDemo2$$Lambda$1(PrintStream var1) {
            this.arg$1 = var1;
        }
    
        private static LambdaPrintFunction get$Lambda(PrintStream var0) {
            return new LambdaDemo2$$Lambda$1(var0);
        }
    
        @Hidden
        public void print(String var1) {
            this.arg$1.println(var1);
        }
    }
    

    此时生成的情况会变得稍显复杂,那么生成的规则到底在哪可以看呢?我们不妨带上附加信息后再看看我们的字节码

    javap -c -p -v target.classes.com.nineyang.LambdaDemo2
    
    SourceFile: "LambdaDemo2.java"
    InnerClasses:
         public static final #56= #55 of #61; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
    BootstrapMethods:
      0: #29 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
        Method arguments:
          #30 (Ljava/lang/String;)V
          #31 invokevirtual java/io/PrintStream.println:(Ljava/lang/String;)V
          #30 (Ljava/lang/String;)V
    

    我们拿到最后的附加信息之后,发现又一个非常有用的提示:invokestatic java/lang/invoke/LambdaMetafactory.metafactory

    //LambdaMetafactory.metafactory方法
        public static CallSite metafactory(MethodHandles.Lookup caller,
                                           String invokedName,
                                           MethodType invokedType,
                                           MethodType samMethodType,
                                           MethodHandle implMethod,
                                           MethodType instantiatedMethodType)
                throws LambdaConversionException {
            AbstractValidatingLambdaMetafactory mf;
            mf = new InnerClassLambdaMetafactory(caller, invokedType,
                                                 invokedName, samMethodType,
                                                 implMethod, instantiatedMethodType,
                                                 false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
            mf.validateMetafactoryArgs();
            return mf.buildCallSite();
        }
    

    该方法会帮助调度InnerClassLambdaMetafactory来我们生成实现类,而InnerClassLambdaMetafactory中,就有了我们所需要的参数相关的生成规则。

        public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
                                           MethodType invokedType,
                                           String samMethodName,
                                           MethodType samMethodType,
                                           MethodHandle implMethod,
                                           MethodType instantiatedMethodType,
                                           boolean isSerializable,
                                           Class<?>[] markerInterfaces,
                                           MethodType[] additionalBridges)
                throws LambdaConversionException {
            super(caller, invokedType, samMethodName, samMethodType,
                  implMethod, instantiatedMethodType,
                  isSerializable, markerInterfaces, additionalBridges);
            implMethodClassName = implDefiningClass.getName().replace('.', '/');
            implMethodName = implInfo.getName();
            implMethodDesc = implMethodType.toMethodDescriptorString();
            implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial)
                    ? implDefiningClass
                    : implMethodType.returnType();
            constructorType = invokedType.changeReturnType(Void.TYPE);
            lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
            cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
            int parameterCount = invokedType.parameterCount();
            if (parameterCount > 0) {
                argNames = new String[parameterCount];
                argDescs = new String[parameterCount];
                for (int i = 0; i < parameterCount; i++) {
                    argNames[i] = "arg$" + (i + 1);
                    argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
                }
            } else {
                argNames = argDescs = EMPTY_STRING_ARRAY;
            }
        }
    

    好了,这就是lambda表达式的基本原理。前面我们用到了自定义的函数式编程,不过坦率地讲,我们在实际工作中用的非常少,因为Java已经帮我们定义好了我们可能会用到的一些接口。

    四大函数式编程接口

    Consumer

    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
    
        default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    }
    

    Consumer是对数据源的操作,没有返回值,同时提供了andThen方法来帮助我们链式调用。这里需要说明的是,注解@FunctionalInterface是可以有默认方法和静态方法的,但是,接口实现只能有一个

    public class ConsumerDemo {
        public static void main(String[] args) {
            Consumer<String> consumer = (name) -> System.out.println("hello," + name);
            consumer.accept("nineyang");
        }
    }
    

    Predicate

    @FunctionalInterface
    public interface Predicate<T> {
    
        boolean test(T t);
    
        default Predicate<T> and(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) && other.test(t);
        }
    
        default Predicate<T> negate() {
            return (t) -> !test(t);
        }
    
        default Predicate<T> or(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) || other.test(t);
        }
    
        static <T> Predicate<T> isEqual(Object targetRef) {
            return (null == targetRef)
                    ? Objects::isNull
                    : object -> targetRef.equals(object);
        }
    }
    

    Predicate,顾名思义,用于判定表达式执行的结果,返回值是boolean

    public class PredicateDemo {
        public static void main(String[] args) {
            Predicate<Integer> predicate = (x) -> x > 0;
            boolean result = predicate.test(10);
        }
    }
    

    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;
        }
    }
    

    Function,可以执行一段逻辑,比如用于转化类型,将一种类型转换为另外一种。

    public class FunctionDemo {
    
        public static void main(String[] args) {
            Function<String,Integer> function = Integer::valueOf;
            Integer result = function.apply("10");
        }
    }
    

    Supplier

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

    Supplier,用于获取结果。

    public class SupplierDemo {
        public static void main(String[] args) {
            Supplier<String> supplier = () -> "hello,nineyang";
            System.out.println(supplier.get());
        }
    }
    
    

    这就是Java中最常用的四大函数式编程的接口了,其他的也基本上是围绕这四种来展开的,这也与我们接下来关于Stream的篇幅内容息息相关,但是由于篇幅有限,这里便不再赘述。

    欢迎关注我的公众号,每周至少一篇比较有深度的原创文章:


    image.png

    相关文章

      网友评论

        本文标题:Java函数式编程的前生今世

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