美文网首页
Java Lambda及InvokedDynamic调用探秘(三

Java Lambda及InvokedDynamic调用探秘(三

作者: gancheng_wxh | 来源:发表于2018-01-03 15:02 被阅读27次

    这次我们来探究一下其它情况。

    package com.github.wanggancheng;
    import java.util.function.Consumer;
    public class InvokeDynamicDemo {
    
        public static void output(String s){
    
            System.out.println(s);
        }
    
        public static void main(String[] args) {
    
            Consumer<String> consumer=InvokeDynamicDemo::output;
            consumer.accept("Lambda demo");
        }
    }
    

    从上面的代码可以看出,与先前的主要差异为:用类静态方法来替代java Lambda。我们反编译class文件的方法信息如下:

    public class com.github.wanggancheng.InvokeDynamicDemo {
      public com.github.wanggancheng.InvokeDynamicDemo();
      public static void output(java.lang.String);
      public static void main(java.lang.String[]);
    }
    

    从上面的信息来看,并没有自动添加什么方法。我们来看看main方法的字节码信息。

     public static void main(java.lang.String[]);
        Code:
           0: invokedynamic #4,  0              // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;
           5: astore_1
           6: aload_1
           7: ldc           #5                  // String Lambda demo
           9: invokeinterface #6,  2            // InterfaceMethod java/util/function/Consumer.accept:(Ljava/lang/Object;)V
          14: return
    

    从上面可以看出第一条指令还是invokedynamic,有先前的例子没有什么差别。我们来看更具体的字节码信息。

    BootstrapMethods:
      0: #36 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:
          #37 (Ljava/lang/Object;)V
          #38 invokestatic com/github/wanggancheng/InvokeDynamicDemo.output:(Ljava/lang/String;)V
          #39 (Ljava/lang/String;)V
    

    从第1个bootstrapmethod来看,与第1篇文章中的示例的bootstrapmethod的差异是第2个参数。先前的实例中第2个参数是调用编译器自动生成的一个静态方法ambda$main$0。

    我们来看看一个稍复杂的例子。

    package com.github.wanggancheng;
    import java.util.function.Consumer;
    public class InvokeDynamicInstanceMethodDemo {
    
        public void output(String s){
    
            System.out.println(s);
        }
    
        public static void main(String[] args) {
    
            InvokeDynamicInstanceMethodDemo instance = new InvokeDynamicInstanceMethodDemo();
            Consumer<String> consumer=instance::output;
            consumer.accept("Lambda demo");
        }
    }
    

    反编译此类的class文件,可以发现也没有添加新方法。还是重点来看看bootstrap_method方法信息。

    BootstrapMethods:
      0: #41 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:
          #42 (Ljava/lang/Object;)V
          #43 invokevirtual com/github/wanggancheng/InvokeDynamicInstanceMethodDemo.output:(Ljava/lang/String;)V
          #44 (Ljava/lang/String;)V
    

    重点差别为#43,表明调用类型为invokevirtual而非invokestatic。

    invokedynamice_info的信息如下:

    #7 = InvokeDynamic      #0:#45         // #0:accept:(Lcom/github/wanggancheng/InvokeDynamicInstanceMethodDemo;)Ljava/util/function/Consumer;
    

    从上面的信息可以看出,与先前的示例的主要差别为invokedType中包含实例方法引用的类实例作为参数。

    我们在运行时把自动生成的类dump出来,反编译信息如下。

    package com.github.wanggancheng;
    
    import java.lang.invoke.LambdaForm.Hidden;
    import java.util.function.Consumer;
    
    // $FF: synthetic class
    final class InvokeDynamicInstanceMethodDemo$$Lambda$1 implements Consumer {
        private final InvokeDynamicInstanceMethodDemo arg$1;
    
        private InvokeDynamicInstanceMethodDemo$$Lambda$1(InvokeDynamicInstanceMethodDemo var1) {
            this.arg$1 = var1;
        }
    
        private static Consumer get$Lambda(InvokeDynamicInstanceMethodDemo var0) {
            return new InvokeDynamicInstanceMethodDemo$$Lambda$1(var0);
        }
    
        @Hidden
        public void accept(Object var1) {
            this.arg$1.output((String)var1);
        }
    }
    

    这个自动合成的类之构造方法有一个InvokeDynamicInstanceMethodDemo。这个参数就是Consumer引用的实例方法对应的类实例。

    跟踪到InnerClassLambdaMetaFactory的buildCallSite方法。

    CallSite最终指向的Method为NAME_FACTORY。这个常量固定为"get$Lambda"。

    相关文章

      网友评论

          本文标题:Java Lambda及InvokedDynamic调用探秘(三

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