精华内容
下载资源
问答
  • 一、前言 java8中,Lambda表达式是匿名函数的一种语法糖,简化了匿名内部类的冗杂代码 java8中,每一个Lambda表达式必须有一个函数式接口与之对应二、语法三、使用举例前置:函数式接口//数学操作 声明参数类型...

    f845b7be31563bb2919cc51ab3c3fa40.png

    一、前言

    • java8中,Lambda表达式是匿名函数的一种语法糖,简化了匿名内部类的冗杂代码
    • java8中,每一个Lambda表达式必须有一个函数式接口与之对应

    二、语法

    9f136a3812456429852150cb13065441.png

    三、使用举例

    前置:函数式接口

    //数学操作
    @FunctionalInterface
    public interface MathOperation {
        int operation(int a, int b);
        //默认实现
        default int addition(int a, int b) {
            return a + b;
        }
    }
    • 声明参数类型
    MathOperation addition = (int a, int b) -> a + b;
    • 不声明参数类型 java MathOperation subtraction = (a, b) -> a - b;
    • 返回语句块
    MathOperation multiplication = (a, b) -> {
                System.out.println("this is multiplication");
                return a * b;
            };
    • 配合Predicate
    //筛选符合predicate的值
    private static void filter(List<Integer> list, Predicate<Integer> predicate) {
        for (Integer n : list) {
            if (predicate.apply(n)) {
                System.out.print(n + " ");
            }
        }
    }
    //筛选出1到5之间的偶数
    filter(ImmutableList.of(1, 2, 3, 4, 5), (n) -> n != null && n % 2 == 0)
    //输出2 4

    四、注意事项:

    • 不能在lambda内部修改定义在域外的变量,可以读取
    int a = 0;
        new Thread(
               () -> a++
               //Variable used in lambda expression should be final or effectively final
        ).start();
    • 当代码体不修改Lambda表达式提供的参数时候,代码体可以替换为方法引用,如果修改参数的时候只能使用Lambda表达式
    List<String> aList = ImmutableList.of("1", "2", "3");
        aList.forEach(string -> System.out.println(string));
        aList.forEach(System.out::println);//可以使用方法引用
        aList.forEach(string -> System.out.println("value:" + string))//只能使用Lambda表达式

    五、原理介绍

    1、测试源码

    public class LambdaTest1 {
        public static void main(String[] args) {
            new Thread(
                    () -> System.out.println("this is lambda")
            );
        }
    }

    2、反编译字节码私有成员

    结果如下:

    public class com.xin.lambda.LambdaTest1 {
      public com.xin.lambda.LambdaTest1();
      public static void main(java.lang.String[]);
      private static void lambda$main$0();
    }

    从反编译结果中可以看到,测试类中增加了一个私有静态方法lambda$main$0()。这一私有静态方法提供给main方法调用,这种实现方式和匿名内部类差别较大,后者编译后生成两个class文件。

    3、查看更详细的字节码信息

    Classfile /C:/Users/69040/Desktop/LambdaTest1.class
      Last modified 2018-8-23; size 1184 bytes
      MD5 checksum ea1e2b1d14787a49549f6984174f1b37
      Compiled from "LambdaTest1.java"
    public class com.xin.lambda.LambdaTest1
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #9.#24         // java/lang/Object."<init>":()V
       #2 = Class              #25            // java/lang/Thread
       #3 = InvokeDynamic      #0:#30         // #0:run:()Ljava/lang/Runnable;
       #4 = Methodref          #2.#31         // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
       #5 = Fieldref           #32.#33        // java/lang/System.out:Ljava/io/PrintStream;
       #6 = String             #34            // this is lambda
       #7 = Methodref          #35.#36        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #8 = Class              #37            // com/xin/lambda/LambdaTest1
       #9 = Class              #38            // java/lang/Object
      #10 = Utf8               <init>
      #11 = Utf8               ()V
      #12 = Utf8               Code
      #13 = Utf8               LineNumberTable
      #14 = Utf8               LocalVariableTable
      #15 = Utf8               this
      #16 = Utf8               Lcom/xin/lambda/LambdaTest1;
      #17 = Utf8               main
      #18 = Utf8               ([Ljava/lang/String;)V
      #19 = Utf8               args
      #20 = Utf8               [Ljava/lang/String;
      #21 = Utf8               lambda$main$0
      #22 = Utf8               SourceFile
      #23 = Utf8               LambdaTest1.java
      #24 = NameAndType        #10:#11        // "<init>":()V
      #25 = Utf8               java/lang/Thread
      #26 = Utf8               BootstrapMethods
      #27 = MethodHandle       #6:#39         // 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;
      #28 = MethodType         #11            //  ()V
      #29 = MethodHandle       #6:#40         // invokestatic com/xin/lambda/LambdaTest1.lambda$main$0:()V
      #30 = NameAndType        #41:#42        // run:()Ljava/lang/Runnable;
      #31 = NameAndType        #10:#43        // "<init>":(Ljava/lang/Runnable;)V
      #32 = Class              #44            // java/lang/System
      #33 = NameAndType        #45:#46        // out:Ljava/io/PrintStream;
      #34 = Utf8               this is lambda
      #35 = Class              #47            // java/io/PrintStream
      #36 = NameAndType        #48:#49        // println:(Ljava/lang/String;)V
      #37 = Utf8               com/xin/lambda/LambdaTest1
      #38 = Utf8               java/lang/Object
      #39 = Methodref          #50.#51        // 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;
      #40 = Methodref          #8.#52         // com/xin/lambda/LambdaTest1.lambda$main$0:()V
      #41 = Utf8               run
      #42 = Utf8               ()Ljava/lang/Runnable;
      #43 = Utf8               (Ljava/lang/Runnable;)V
      #44 = Utf8               java/lang/System
      #45 = Utf8               out
      #46 = Utf8               Ljava/io/PrintStream;
      #47 = Utf8               java/io/PrintStream
      #48 = Utf8               println
      #49 = Utf8               (Ljava/lang/String;)V
      #50 = Class              #53            // java/lang/invoke/LambdaMetafactory
      #51 = NameAndType        #54:#58        // 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;
      #52 = NameAndType        #21:#11        // lambda$main$0:()V
      #53 = Utf8               java/lang/invoke/LambdaMetafactory
      #54 = Utf8               metafactory
      #55 = Class              #60            // java/lang/invoke/MethodHandles$Lookup
      #56 = Utf8               Lookup
      #57 = Utf8               InnerClasses
      #58 = Utf8               (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;
      #59 = Class              #61            // java/lang/invoke/MethodHandles
      #60 = Utf8               java/lang/invoke/MethodHandles$Lookup
      #61 = Utf8               java/lang/invoke/MethodHandles
    {
      public com.xin.lambda.LambdaTest1();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 11: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/xin/lambda/LambdaTest1;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=1, args_size=1
             0: new           #2                  // class java/lang/Thread
             3: dup
             4: invokedynamic #3,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
             9: invokespecial #4                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
            12: pop
            13: return
          LineNumberTable:
            line 13: 0
            line 16: 13
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      14     0  args   [Ljava/lang/String;
    }
    SourceFile: "LambdaTest1.java"
    InnerClasses:
         public static final #56= #55 of #59; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
    BootstrapMethods:
      0: #27 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:
          #28 ()V
          #29 invokestatic com/xin/lambda/LambdaTest1.lambda$main$0:()V
          #28 ()V
    • 代码中使用Lambda表达式的语句被编译成“ 4: invokedynamic #3, 0”,指向常量池#3位置
    • invokedynamic在JVM规范中有如下规定
    1操作码
    invokedynamic = 186 (0xba)
    2参数
    indexbyte1 indexbyte2 0 0,前两个参数构造索引(indexbyte1 << 8) | indexbyte2),后两个参数必须为0。(the unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class (§2.6), where the value of the index is (indexbyte1 << 8) | indexbyte2. The run-time constant pool item at that index must be a symbolic reference to a call site specifier (§5.1). The values of the third and fourth operand bytes must always be zero. 
    3索引指向的常量池项的类型为CONSTANT_InvokeDynamic_info
    • CONSTANT_InvokeDynamic_info
    1定义
        CONSTANT_InvokeDynamic_info {
        u1 tag;
        u2 bootstrap_method_attr_index;
        u2 name_and_type_index;
    }
    2参数
    bootstrap_method_attr_index索引bootstrap_methods
    name_and_type_index索引类常量池里的CONSTANT_NameAndType_info
    本例中常量池#3为" #3 = InvokeDynamic      #0:#30 "
        
        bootstrap_method_attr_index为#0
        name_and_type_index为#30其内容为"NameAndType        #41:#42",跟下去后可以得出结果这里描述的是Lambda表达式需要调用的函数"java/lang/Runnable.run()"
    3JVM规定
    如果类常量池中存在CONSTANT_InvokeDynamic_info那么必须有且仅有一个BootstrapMethods_attribute。(There must be exactly one BootstrapMethods attribute in the attributes table of a given ClassFile structure if the constant_pool table of the ClassFile structure has at least one CONSTANT_InvokeDynamic_info entry (§4.4.10). There can be no more than one BootstrapMethods attribute in the attributes table of a given ClassFile structure. 在反编译文件末尾可以看到的确有且仅有一个BootstrapMethods
    4作用
    在java7中引入主要作用给invokedynamic指令制定启动方法
    • BootstrapMethods_attribute
    1定义
    BootstrapMethods_attribute {
        u2 attribute_name_index;
        u4 attribute_length;
        u2 num_bootstrap_methods;
        {   u2 bootstrap_method_ref;
            u2 num_bootstrap_arguments;
            u2 bootstrap_arguments[num_bootstrap_arguments];
        } bootstrap_methods[num_bootstrap_methods];
    }
    2参数
    每一个BootstrapMethod都包含一个bootstrap_method_ref和n个bootstrap_arguments
    本例中
        bootstrap_method_ref指向常量池#27值为MethodHandle       #6:#39”,继续查看jvm规范
    MethodHandle含有两个参数reference_kindreference_indexreference_kind值为6意思是REF_invokeStaticreference_index为39即常量池#39位置内容为Methodref          #50.#51”,表明这里调用的是java/lang/invoke/LambdaMetafactory.metafactory()
    • metafactory函数 1、源码如下
    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();

    2、参数: caller、invokedName、invokedType都由jvm生成,invokedName和invokedType分别是由动态调用点的NameAndType生成,samMethodType和instantiatedMethodType表示该函数式接口的方法描述符,implMethod最为重要,指用户实现的Lambda方法,本例为:com.xin.lambda.LambdaTest1.lambda$main$0()void/invokeStatic

    3、代码快速解读:

    metafactory()核心要生成CallSite,这个任务主要由InnerClassLambdaMetafactory的buildCallSite()完成,buildCallSite()继续调用spinInnerClass()函数,spinInnerClass函数用字节码工具在内存中生成一个类。

    4、到此Lambda表达式的脱糖过程结束

    六、结论

    以上简单的分析了Lambda表达式的脱糖过程,还有很多细节需要各位大神去研究,总之,Lambda表达式的实现原理依然是基于内部类的方式。

    展开全文
  • 主要介绍了Python lambda表达式原理及用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • Lambda表达式原理简介

    2020-03-26 14:29:07
    Lambda表达式原理简介 一、前言 java8中,Lambda表达式是匿名函数的一种语法糖,简化了匿名内部类的冗杂代码 java8中,每一个Lambda表达式必须有一个函数式接口与之对应 二、语法 参数列表 操作符 代码体...

    Lambda表达式原理简介

    一、前言

    • java8中,Lambda表达式是匿名函数的一种语法糖,简化了匿名内部类的冗杂代码

    • java8中,每一个Lambda表达式必须有一个函数式接口与之对应

    二、语法

    参数列表 操作符 代码体(表达式/代码块)
    (int x,int y) -> x+y
    (x,y) -> x+y
    (int x,int y) -> {return x+y;}
    (x,y) -> {return x+y;}

    三、使用举例

    前置:函数式接口

    //数学操作
    @FunctionalInterface
    public interface MathOperation {
        int operation(int a, int b);
        //默认实现
        default int addition(int a, int b) {
            return a + b;
        }
    }
    
    • 声明参数类型
    MathOperation addition = (int a, int b) -> a + b;
    
    • 不声明参数类型
      MathOperation subtraction = (a, b) -> a - b;
      
    • 返回语句块
    MathOperation multiplication = (a, b) -> {
                System.out.println("this is multiplication");
                return a * b;
            };
    
    • 配合Predicate
    //筛选符合predicate的值
    private static void filter(List<Integer> list, Predicate<Integer> predicate) {
        for (Integer n : list) {
            if (predicate.apply(n)) {
                System.out.print(n + " ");
            }
        }
    }
    //筛选出1到5之间的偶数
    filter(ImmutableList.of(1, 2, 3, 4, 5), (n) -> n != null && n % 2 == 0)
    //输出:2 4
    

    四、注意事项:

    • 不能在lambda内部修改定义在域外的变量,可以读取
        int a = 0;
        new Thread(
               () -> a++
               //Variable used in lambda expression should be final or effectively final
        ).start();
    
    • 当代码体不修改Lambda表达式提供的参数时候,代码体可以替换为方法引用,如果修改参数的时候只能使用Lambda表达式
        List<String> aList = ImmutableList.of("1", "2", "3");
        aList.forEach(string -> System.out.println(string));
        aList.forEach(System.out::println);//可以使用方法引用
        aList.forEach(string -> System.out.println("value:" + string))//只能使用Lambda表达式
    

    五、原理介绍

    1、测试源码
    public class LambdaTest1 {
        public static void main(String[] args) {
            new Thread(
                    () -> System.out.println("this is lambda")
            );
        }
    }
    
    2、反编译字节码私有成员

    结果如下:

    public class com.xin.lambda.LambdaTest1 {
      public com.xin.lambda.LambdaTest1();
      public static void main(java.lang.String[]);
      private static void lambda$main$0();
    }
    

    从反编译结果中可以看到,测试类中增加了一个私有静态方法lambda$main$0()。这一私有静态方法提供给main方法调用,这种实现方式和匿名内部类差别较大,后者编译后生成两个class文件。

    3、查看更详细的字节码信息
    Classfile /C:/Users/69040/Desktop/LambdaTest1.class
      Last modified 2018-8-23; size 1184 bytes
      MD5 checksum ea1e2b1d14787a49549f6984174f1b37
      Compiled from "LambdaTest1.java"
    public class com.xin.lambda.LambdaTest1
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #9.#24         // java/lang/Object."<init>":()V
       #2 = Class              #25            // java/lang/Thread
       #3 = InvokeDynamic      #0:#30         // #0:run:()Ljava/lang/Runnable;
       #4 = Methodref          #2.#31         // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
       #5 = Fieldref           #32.#33        // java/lang/System.out:Ljava/io/PrintStream;
       #6 = String             #34            // this is lambda
       #7 = Methodref          #35.#36        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #8 = Class              #37            // com/xin/lambda/LambdaTest1
       #9 = Class              #38            // java/lang/Object
      #10 = Utf8               <init>
      #11 = Utf8               ()V
      #12 = Utf8               Code
      #13 = Utf8               LineNumberTable
      #14 = Utf8               LocalVariableTable
      #15 = Utf8               this
      #16 = Utf8               Lcom/xin/lambda/LambdaTest1;
      #17 = Utf8               main
      #18 = Utf8               ([Ljava/lang/String;)V
      #19 = Utf8               args
      #20 = Utf8               [Ljava/lang/String;
      #21 = Utf8               lambda$main$0
      #22 = Utf8               SourceFile
      #23 = Utf8               LambdaTest1.java
      #24 = NameAndType        #10:#11        // "<init>":()V
      #25 = Utf8               java/lang/Thread
      #26 = Utf8               BootstrapMethods
      #27 = MethodHandle       #6:#39         // 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;
      #28 = MethodType         #11            //  ()V
      #29 = MethodHandle       #6:#40         // invokestatic com/xin/lambda/LambdaTest1.lambda$main$0:()V
      #30 = NameAndType        #41:#42        // run:()Ljava/lang/Runnable;
      #31 = NameAndType        #10:#43        // "<init>":(Ljava/lang/Runnable;)V
      #32 = Class              #44            // java/lang/System
      #33 = NameAndType        #45:#46        // out:Ljava/io/PrintStream;
      #34 = Utf8               this is lambda
      #35 = Class              #47            // java/io/PrintStream
      #36 = NameAndType        #48:#49        // println:(Ljava/lang/String;)V
      #37 = Utf8               com/xin/lambda/LambdaTest1
      #38 = Utf8               java/lang/Object
      #39 = Methodref          #50.#51        // 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;
      #40 = Methodref          #8.#52         // com/xin/lambda/LambdaTest1.lambda$main$0:()V
      #41 = Utf8               run
      #42 = Utf8               ()Ljava/lang/Runnable;
      #43 = Utf8               (Ljava/lang/Runnable;)V
      #44 = Utf8               java/lang/System
      #45 = Utf8               out
      #46 = Utf8               Ljava/io/PrintStream;
      #47 = Utf8               java/io/PrintStream
      #48 = Utf8               println
      #49 = Utf8               (Ljava/lang/String;)V
      #50 = Class              #53            // java/lang/invoke/LambdaMetafactory
      #51 = NameAndType        #54:#58        // 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;
      #52 = NameAndType        #21:#11        // lambda$main$0:()V
      #53 = Utf8               java/lang/invoke/LambdaMetafactory
      #54 = Utf8               metafactory
      #55 = Class              #60            // java/lang/invoke/MethodHandles$Lookup
      #56 = Utf8               Lookup
      #57 = Utf8               InnerClasses
      #58 = Utf8               (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;
      #59 = Class              #61            // java/lang/invoke/MethodHandles
      #60 = Utf8               java/lang/invoke/MethodHandles$Lookup
      #61 = Utf8               java/lang/invoke/MethodHandles
    {
      public com.xin.lambda.LambdaTest1();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 11: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/xin/lambda/LambdaTest1;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=1, args_size=1
             0: new           #2                  // class java/lang/Thread
             3: dup
             4: invokedynamic #3,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
             9: invokespecial #4                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
            12: pop
            13: return
          LineNumberTable:
            line 13: 0
            line 16: 13
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      14     0  args   [Ljava/lang/String;
    }
    SourceFile: "LambdaTest1.java"
    InnerClasses:
         public static final #56= #55 of #59; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
    BootstrapMethods:
      0: #27 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:
          #28 ()V
          #29 invokestatic com/xin/lambda/LambdaTest1.lambda$main$0:()V
          #28 ()V
    
    • 代码中使用Lambda表达式的语句被编译成“ 4: invokedynamic #3, 0”,指向常量池#3位置
    • invokedynamic在JVM规范中有如下规定
    1、操作码:
    invokedynamic = 186 (0xba)
    2、参数:
    indexbyte1 indexbyte2 0 0,前两个参数构造索引((indexbyte1 << 8) | indexbyte2),后两个参数必须为0。(the unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class (§2.6), where the value of the index is (indexbyte1 << 8) | indexbyte2. The run-time constant pool item at that index must be a symbolic reference to a call site specifier (§5.1). The values of the third and fourth operand bytes must always be zero.3、索引指向的常量池项的类型为CONSTANT_InvokeDynamic_info
    
    • CONSTANT_InvokeDynamic_info
    1、定义:
        CONSTANT_InvokeDynamic_info {
        u1 tag;
        u2 bootstrap_method_attr_index;
        u2 name_and_type_index;
    }
    2、参数:
    bootstrap_method_attr_index:索引bootstrap_methods
    name_and_type_index:索引类常量池里的CONSTANT_NameAndType_info
    本例中常量池#3为:" #3 = InvokeDynamic      #0:#30 "
        即
        bootstrap_method_attr_index为#0
        name_and_type_index为#30,其内容为"NameAndType        #41:#42",跟下去后可以得出结果,这里描述的是Lambda表达式需要调用的函数:"java/lang/Runnable.run()"
    3、JVM规定:
    如果类常量池中存在CONSTANT_InvokeDynamic_info,那么必须有且仅有一个BootstrapMethods_attribute。(There must be exactly one BootstrapMethods attribute in the attributes table of a given ClassFile structure if the constant_pool table of the ClassFile structure has at least one CONSTANT_InvokeDynamic_info entry (§4.4.10). There can be no more than one BootstrapMethods attribute in the attributes table of a given ClassFile structure. )在反编译文件末尾可以看到的确有且仅有一个BootstrapMethods。
    4、作用:
    在java7中引入,主要作用给invokedynamic指令制定启动方法
    
    • BootstrapMethods_attribute
    1、定义:
    BootstrapMethods_attribute {
        u2 attribute_name_index;
        u4 attribute_length;
        u2 num_bootstrap_methods;
        {   u2 bootstrap_method_ref;
            u2 num_bootstrap_arguments;
            u2 bootstrap_arguments[num_bootstrap_arguments];
        } bootstrap_methods[num_bootstrap_methods];
    }
    2、参数:
    每一个BootstrapMethod都包含一个bootstrap_method_ref和n个bootstrap_arguments
    本例中:
        bootstrap_method_ref指向常量池#27,值为“MethodHandle       #6:#39”,继续查看jvm规范,
    MethodHandle含有两个参数reference_kind、reference_index,reference_kind值为6,意思是REF_invokeStatic,reference_index为39,即常量池#39位置,内容为“Methodref          #50.#51”,表明这里调用的是java/lang/invoke/LambdaMetafactory.metafactory()
    
    • metafactory函数
      1、源码如下
    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();
    

    2、参数:
    caller、invokedName、invokedType都由jvm生成,invokedName和invokedType分别是由动态调用点的NameAndType生成,samMethodType和instantiatedMethodType表示该函数式接口的方法描述符,implMethod最为重要,指用户实现的Lambda方法,本例为:com.xin.lambda.LambdaTest1.lambda$main$0()void/invokeStatic

    3、代码快速解读:

    metafactory()核心要生成CallSite,这个任务主要由InnerClassLambdaMetafactory的buildCallSite()完成,buildCallSite()继续调用spinInnerClass()函数,spinInnerClass函数用字节码工具在内存中生成一个类。

    4、到此Lambda表达式的脱糖过程结束

    六、结论

    以上简单的分析了Lambda表达式的脱糖过程,还有很多细节需要各位大神去研究,总之,Lambda表达式的实现原理依然是基于内部类的方式。

    展开全文
  • Java Lambda表达式原理及多线程实现1、使用Lambda表达式实现多线程public static void main(String[] args) {//使用匿名内部类的方式,实现多线程new Thread(new Runnable() {@Overridepublic void run() {System....

    Java Lambda表达式原理及多线程实现

    1、使用Lambda表达式实现多线程

    public static void main(String[] args) {

    //使用匿名内部类的方式,实现多线程

    new Thread(new Runnable() {

    @Override

    public void run() {

    System.out.println(Thread.currentThread().getName() + "新线程创建了!");

    }

    }).start();

    //使用Lambda表达式,实现多线程

    new Thread(() -> {

    System.out.println(Thread.currentThread().getName() + "新线程创建了!");

    }

    ).start();

    }

    2、Lambda表达式的标准格式

    (参数列表,用逗号隔开)-> {一些重写方法的代码};

    3、Lambda的作用和目的:

    简化匿名内部类的书写。

    4、Lambda表达式继续省略(凡是根据上下文可以推导出来的内容,都可以省略):

    a、(参数列表):括号中的参数列表的数据类型,可以省略不写;

    b、(参数列表):括号中的参数如果只有一个,那么数据类型和小括号( )都可以省略;

    c、{一些代码}:如果大括号{ }中的代码只有一行,就可以省略大括号{ },return和分号

    注意:要省略必须三个一起省略。

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

    时间: 2020-07-02

    6b5aa0751a6f1e6a9d0b665a02cc1f46.gif

    前言 最近在学习java8,所以接下来会给大家介绍一系列的Java8学习内容,那么让我们先从lambda表达式开始. 众所周知从java8出现以来lambda是最重要的特性之一,它可以让我们用简洁流畅的代码完成一个功能. 很长一段时间java被吐槽是冗余和缺乏函数式编程能力的语言,随着函数式编程的流行java8种也引入了 这种编程风格.在此之前我们都在写匿名内部类干这些事,但有时候这不是好的做法,本文中将介绍和使用lambda, 带你体验函数式编程的魔力. 什么是lambda? lambda表达

    本文实例讲述了Java Lambda表达式的方法引用和构造器引用.分享给大家供大家参考,具体如下: 一 点睛 如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用,以使得Lambda表达式更加简洁. 种类 示例 说明 对应的Lambda表达式 引用类方法 类名::类方法 函数式接口中被实现方法的全部参数传给该类方法作为参数. (a,b,...) -> 类名.类方法(a,b, ...) 引用特定对象的实例方法 特定对象::实例方法 函数式接口中被实现方法的全部参数传

    需求:按照起始日期查询出数据库里一段连续日期的住院信息. 问题:数据库里的住院信息可能不是完整的,也就是在给出的日期区间里只有若干天的数据,缺少某些日期的数据. 解决: 1.需要我们先按日期分组查出数据库里有的数据: 2.然后遍历日期,将不存在的日期以日期为key,value为null插入集合里: 3.对集合里的key即日期进行排序. 注:这里分组和排序都用JDK8的新特性lambda表达式 /** * * @param startTime 开始时间 * @param endTime 结束时间

    本文实例讲述了Java Lambda表达式与匿名内部类的联系和区别.分享给大家供大家参考,具体如下: 一 点睛 Lambda表达式与匿名内部类存在如下相同点: Lambda表达式与匿名内部类一样,都可以直接访问"effectively final"的局部变量,以及外部类的成员变量(包括实例变量和类变量). Lambda表达式创建的对象与匿名内部类生成的对象一样, 都可以直接调用从接口继承得到的默认方法. Lambda表达式与匿名内部类主要存在如下区别: 匿名内部类可以为任意接口创建实例

    6b5aa0751a6f1e6a9d0b665a02cc1f46.gif

    这篇文章主要介绍了java lamda表达式用法总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.什么是函数式编程(百度百科上的解释) 2.为什么要使用函数式编程(有什么好处) 1.代码简洁,减少代码量 2.接近自然语言,容易理解 传统实现分组 List students; Map> maps = Maps.newHashMap(); for(Stude

    Lambda用到了JDK8自带的一个函数式接口Comparator. 准备一个Apple类 public class Apple { private int weight; private String color; public Apple(){} public Apple(int weight) { this.weight = weight; } public Apple(int weight, String color) { this.weight = weight; this.c

    具体代码如下所示: import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; public class LambdaStudy { public static void main(String[] args) { //初始化list集合 List list = new ArrayList&l

    从集合到流 现在我们用代码来具体表示对某一集合进行迭代操作,我们希望定义一个Contact类来表示联系人,并将ContactList中所有String类型的联系人姓名全部包装进Contact类中: List contacts = new ArrayList<>(); contactList.forEach(new Consumer() { @Override public void accept(String s) { Contact

    Java 8 lambda表达式引入详解及实例 eclipse 下载安装 Help -> EclipseMarketplace -> 搜索Java 8 Kepler ->Java 8 support for eclipse Kepler SR2 安装完成后需要重启 Android Studio 在project的build.gradle文件中添加 buildscript { dependencies { classpath 'me.tatarka:gradle-retrolambda:3

    我对java中lambda表达式的看法是相当纠结的: 一个我这么想:lambda表达式降低了java程序的阅读体验.java程序一直不以表现力出众,正相反使Java流行的一个因素正是它的安全和保守--即使是初学者只要注意些也能写出健壮且容易维护的代码来.lambda表达式对开发人员的要求相对来说高了一层,因此也增加了一些维护难度. 另一个我这么想:作为一个码代码的,有必要学习并接受语言的新特性.如果只是因为它的阅读体验差就放弃它在表现力方面的长处,那么即使是三目表达式也有人觉得理解起来困难呢.语

    从串行到并行 串行指一个步骤一个步骤地处理,也就是通常情况下,代码一行一行地执行. 如果将我们常用的迭代器式的循环展开的话,就是串行执行了循环体内所定义的操作: sum += arr.get(0); sum += arr.get(1); sum += arr.get(2); //... 在书的一开始,就提到Java需要支持集合的并行计算(而Lambda为这个需求提供了可能). 这些功能将全部被实现于库代码中,对于我们使用者,实现并行的复杂性被大大降低(最低程度上只需要调用相关方法). 另外,关于

    当我们需要将DTO转换为实体(Hibernate实体等)并向后转换时,我们都会面临混乱的开销代码. 在我的示例中,我将用Java 8演示代码如何变得越来越短. 让我们创建目标DTO: public class ActiveUserListDTO { public ActiveUserListDTO() { } public ActiveUserListDTO(UserEntity userEntity) { this.username = userEntity.getUsername(); ..

    接上一篇:初探Lambda表达式/Java多核编程[2]并行与组合行为 本节是第二章开篇,前一章已经浅显地将所有新概念点到,书中剩下的部分将对这些概念做一个基础知识的补充与深入探讨实践. 本章将介绍Lambda表达式基础知识. 前言 把上一张书中的结语放到这里作为本章学习内容的开头,以此来概括Lambda表达式的优点: 提升性能.自动的并行化 更棒的API(comparing(...)细粒度的方法将成为标准) 编码风格得到改进.代码简化 反观前面几篇文章中的代码实践,以上三个优点全部得到了验证.

    Java8引入了lambda表达式.lambda表达式并不是新功能,只是为了方便代码编写的语法糖. 但,即便是在其他语言已经司空见惯的lambda表达式,如果在Java中要支持它,还需要考虑各种向下兼容的问题. 简单的说,Java的lambda表达式支持,大约需要考虑2个方面 需要支持lambda语法,以替代原有的方法匿名类 需要考虑已有JDK中,如何增加新操作以支持lambda表达式 对于第一点的回答是FuntionalInterface的Annotation,第二点的回答是default方法

    引言 在这个例子中,我们将向您展示如何使用 java8 lambda 表达式编写一个 Comparator 来对 List 进行排序. 经典的比较器示例: Comparator byName = new Comparator() { @Override public int compare(Developer o1, Developer o2) { return o1.getName().compareTo(o2.getName());

    简介 java 8中引入了lambda表达式,lambda表达式可以让我们的代码更加简介,业务逻辑更加清晰,但是在lambda表达式中使用的Functional Interface并没有很好的处理异常,因为JDK提供的这些Functional Interface通常都是没有抛出异常的,这意味着需要我们自己手动来处理异常. 因为异常分为Unchecked Exception和checked Exception,我们分别来讨论. 处理Unchecked Exception Unchecked exc

    展开全文
  • 主要介绍了Java Lambda表达式原理及多线程实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 主要介绍了C# lambda表达式原理定义及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • java8 lambda表达式原理

    千次阅读 2017-09-20 10:58:13
    java8 lambda表达式原理 java8已经推出有一段时间了,相信有不少公司已经把jdk升级到8了,每次jdk的升级都会带来一些性能以及应用上的优化,比如8移出了永久区,java.lang.OutOfMemoryError: PermGen space离我们而...

    java8 lambda表达式原理

    java8已经推出有一段时间了,相信有不少公司已经把jdk升级到8了,每次jdk的升级都会带来一些性能以及应用上的优化,比如8移出了永久区,java.lang.OutOfMemoryError: PermGen space离我们而去,以及一些新的语法糖lambda,stream,默认方法等等,本文就来说说lambda表达式

    lambda表达式写法

    基本语法:
    (parameters) -> expression

    (parameters) ->{ statements; }

    左边为传入参数,右边为执行代码,代表一个函数式接口的实现

    lambda例子

    此处不多写,贴出其他人的博客,请见Java8 lambda表达式10个示例

    lambda表达式与旧的api对比

    • 对比 code
    public class LambdaTest {
    
        public static void main(String[] args) {
            ArrayList<Integer> integers = new ArrayList<Integer>() ;
            integers.add(9);
            integers.add(8);
            integers.add(7);
            integers.add(6);
            integers.sort(new Comparator<Integer>() {
                public int compare(Integer o1, Integer o2) {
                    return o1.compareTo(o2);
                }
            });
            System.out.println("匿名内部类排序输出:"+integers);
    
    
            integers.sort((o1,o2)->o1.compareTo(02));
            System.out.println("lambda1表达式排序输出:"+integers);
    
    
            integers.sort(Integer::compareTo);
            System.out.println("lambda2表达式排序输出:"+integers);
        }
    }
    • 对比 输出
    匿名内部类排序输出:[6, 7, 8, 9]
    lambda1表达式排序输出:[6, 7, 8, 9]
    lambda2表达式排序输出:[6, 7, 8, 9]
    • 编译后的文件
      这里写图片描述
      从上面看到,生成了两个class文件,一个是LambdaTest类的class文件,一个是Comparator匿名内部类的class文件,lambda表达式并未生成匿名内部类class文件,也就是说java8并不是靠编译器将lambda转换为匿名内部类,lambda脱糖过程稍后描述。

    lambda原理

    在java8中每一个Lambda表达式必须有一个函数式接口与之对应,那么函数式接口是什么呢?

    什么是函数式接口

    函数式接口(functional interface)简单来说就是只包含一个抽象方法的普通接口,java.lang.Runnable、java.util.Comparator都是函数式接口,java8提供了java.lang.FunctionalInterface注解进行标准,但是是非必须的,jdk会自动识别函数式接口。函数式接口可以被隐式转换为lambda表达式。

    函数式与lambda表达式关系实例

    • 正确实例
    public class LambdaTest2 {
    
        public interface TestInterface{
            public void test1();
        }
    
        public static void doSomething(TestInterface test){
            test.test1();
        }
        public static void main(String[] args) {
            doSomething(()-> System.out.println("HelloWorld"));
        }
    }

    输出HelloWorld

    • 错误实例
    public class LambdaTest3 {
    
        public interface TestInterface{
            public void test1();
            public void test2(int a);
        }
    
        public static void doSomething(TestInterface test){
            test.test1();
        }
        public static void main(String[] args) {
            doSomething(()-> System.out.println("HelloWorld"));
        }
    }

    编译错误:
    Error:(19, 21) java: 不兼容的类型: com.chen.LambdaTest3.TestInterface 不是函数接口
    在 接口 com.chen.LambdaTest3.TestInterface 中找到多个非覆盖抽象方法

    那么lambda是怎么跟函数式接口对应的呢

    来看一下LambdaTest2的字节码

    Classfile /F:/workspace/java8-test/target/classes/com/chen/LambdaTest2.class
      Last modified 2017-2-18; size 1376 bytes
      MD5 checksum 5de06d632caf9ead069b79cf854411b9
      Compiled from "LambdaTest2.java"
    public class com.chen.LambdaTest2
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #9.#31         // java/lang/Object."<init>":()V
       #2 = InterfaceMethodref #10.#32        // com/chen/LambdaTest2$TestInterface.test1:()V
       #3 = InvokeDynamic      #0:#37         // #0:test1:()Lcom/chen/LambdaTest2$TestInterface;
       #4 = Methodref          #8.#38         // com/chen/LambdaTest2.doSomething:(Lcom/chen/LambdaTest2$TestInterface;)V
       #5 = Fieldref           #39.#40        // java/lang/System.out:Ljava/io/PrintStream;
       #6 = String             #41            // HelloWorld
       #7 = Methodref          #42.#43        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #8 = Class              #44            // com/chen/LambdaTest2
       #9 = Class              #45            // java/lang/Object
      #10 = Class              #46            // com/chen/LambdaTest2$TestInterface
      #11 = Utf8               TestInterface
      #12 = Utf8               InnerClasses
      #13 = Utf8               <init>
      #14 = Utf8               ()V
      #15 = Utf8               Code
      #16 = Utf8               LineNumberTable
      #17 = Utf8               LocalVariableTable
      #18 = Utf8               this
      #19 = Utf8               Lcom/chen/LambdaTest2;
      #20 = Utf8               doSomething
      #21 = Utf8               (Lcom/chen/LambdaTest2$TestInterface;)V
      #22 = Utf8               test
      #23 = Utf8               Lcom/chen/LambdaTest2$TestInterface;
      #24 = Utf8               main
      #25 = Utf8               ([Ljava/lang/String;)V
      #26 = Utf8               args
      #27 = Utf8               [Ljava/lang/String;
      #28 = Utf8               lambda$main$0
      #29 = Utf8               SourceFile
      #30 = Utf8               LambdaTest2.java
      #31 = NameAndType        #13:#14        // "<init>":()V
      #32 = NameAndType        #47:#14        // test1:()V
      #33 = Utf8               BootstrapMethods
      #34 = MethodHandle       #6:#48         // 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;
      #35 = MethodType         #14            //  ()V
      #36 = MethodHandle       #6:#49         // invokestatic com/chen/LambdaTest2.lambda$main$0:()V
      #37 = NameAndType        #47:#50        // test1:()Lcom/chen/LambdaTest2$TestInterface;
      #38 = NameAndType        #20:#21        // doSomething:(Lcom/chen/LambdaTest2$TestInterface;)V
      #39 = Class              #51            // java/lang/System
      #40 = NameAndType        #52:#53        // out:Ljava/io/PrintStream;
      #41 = Utf8               HelloWorld
      #42 = Class              #54            // java/io/PrintStream
      #43 = NameAndType        #55:#56        // println:(Ljava/lang/String;)V
      #44 = Utf8               com/chen/LambdaTest2
      #45 = Utf8               java/lang/Object
      #46 = Utf8               com/chen/LambdaTest2$TestInterface
      #47 = Utf8               test1
      #48 = Methodref          #57.#58        // 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;
      #49 = Methodref          #8.#59         // com/chen/LambdaTest2.lambda$main$0:()V
      #50 = Utf8               ()Lcom/chen/LambdaTest2$TestInterface;
      #51 = Utf8               java/lang/System
      #52 = Utf8               out
      #53 = Utf8               Ljava/io/PrintStream;
      #54 = Utf8               java/io/PrintStream
      #55 = Utf8               println
      #56 = Utf8               (Ljava/lang/String;)V
      #57 = Class              #60            // java/lang/invoke/LambdaMetafactory
      #58 = NameAndType        #61:#64        // 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;
      #59 = NameAndType        #28:#14        // lambda$main$0:()V
      #60 = Utf8               java/lang/invoke/LambdaMetafactory
      #61 = Utf8               metafactory
      #62 = Class              #66            // java/lang/invoke/MethodHandles$Lookup
      #63 = Utf8               Lookup
      #64 = Utf8               (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;
      #65 = Class              #67            // java/lang/invoke/MethodHandles
      #66 = Utf8               java/lang/invoke/MethodHandles$Lookup
      #67 = Utf8               java/lang/invoke/MethodHandles
    {
      public com.chen.LambdaTest2();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 8: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/chen/LambdaTest2;
    
      public static void doSomething(com.chen.LambdaTest2$TestInterface);
        descriptor: (Lcom/chen/LambdaTest2$TestInterface;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokeinterface #2,  1            // InterfaceMethod com/chen/LambdaTest2$TestInterface.test1:()V
             6: return
          LineNumberTable:
            line 15: 0
            line 16: 6
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       7     0  test   Lcom/chen/LambdaTest2$TestInterface;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=1, args_size=1
             0: invokedynamic #3,  0              // InvokeDynamic #0:test1:()Lcom/chen/LambdaTest2$TestInterface;
             5: invokestatic  #4                  // Method doSomething:(Lcom/chen/LambdaTest2$TestInterface;)V
             8: return
          LineNumberTable:
            line 18: 0
            line 19: 8
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       9     0  args   [Ljava/lang/String;
    }
    SourceFile: "LambdaTest2.java"
    InnerClasses:
         public static #11= #10 of #8; //TestInterface=class com/chen/LambdaTest2$TestInterface of class com/chen/LambdaTest2
         public static final #63= #62 of #65; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
    BootstrapMethods:
      0: #34 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:
          #35 ()V
          #36 invokestatic com/chen/LambdaTest2.lambda$main$0:()V
          #35 ()V
    

    其中main方法

     public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=1, args_size=1
             0: invokedynamic #3,  0              // InvokeDynamic #0:test1:()Lcom/chen/LambdaTest2$TestInterface;
             5: invokestatic  #4                  // Method doSomething:(Lcom/chen/LambdaTest2$TestInterface;)V
             8: return
          LineNumberTable:
            line 18: 0
            line 19: 8
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       9     0  args   [Ljava/lang/String;

    执行了三条指令 invokedynamic、invokestatic、return

    • 第一条是lambda表达式转化为函数式接口TestInterface
    • 第二条执行doSomething方法
    • 第三条退出main方法

    invokedynamic指令是在jvm7中新增的,invokedynamic出现的位置代表一个动态调用点
    invokedynamic指令后面会跟一个指向常量池的调用点限定符,这个限定符会被解析为一个动态调用点。
    调用点限定符的符号引用为CONSTANT_InvokeDynamic_info结构

    CONSTANT_InvokeDynamic_info{  
        u1 tag;  
        u2 bootstrap_method_attr_index;   
        u2 name_and_type_index;  
    }  

    依据这个可以找到对应的动态调用引导方法Java.lang.invoke.CallSite

    此处invokedynamic后面跟的是常量#3,#3指向#0和#37,#37代表TestInterface接口的test1方法

    #3 = InvokeDynamic      #0:#37         // #0:test1:()Lcom/chen/LambdaTest2$TestInterface;
    #37 = NameAndType        #47:#50        // test1:()Lcom/chen/LambdaTest2$TestInterface;

    而#0在字节码最后的BootstrapMethods中,Method arguments#36代表这个lambda表达式调用代码

    BootstrapMethods:
      0: #34 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:
          #35 ()V
          #36 invokestatic com/chen/LambdaTest2.lambda$main$0:()V
          #35 ()V

    根据BootstrapMethods对应的#34可以找到此处lambda InvokeDynamic指令对应的引导方法是LambdaMetafactory.metafactory,其返还一个CallSite

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

    观察源码可得,其通过new一个InnerClassLambdaMetafactory并调用buildCallSite方法创造了类似内部类的lambda CallSite
    有兴趣可以看buildCallSite方法的源码
    InnerClassLambdaMetafactory类的源码注释是

    Lambda metafactory implementation which dynamically creates an inner-class-like class per lambda callsite.

    至此,lambda表达式的脱糖过程已经了解完成,其中很多细节设计太多java字节码知识,本汪也不是太了解,有兴趣可以多看看java虚拟机规范

    至于为什么要绕一圈生成一个内部类的动态调用点然后执行,而不是直接把lambda编译成内部类,也许是为了减少编译后的文件数,具体不得而知,有待研究

    展开全文
  • 开篇先明义:lambda表达式,而def函数是语句代码块所以lambda其实就是一个稍微高级一点的式子而已,只不过这个式子比较长,而且还会自己进行一些逻辑上的处理而已,心里不要虚lambda也被叫做匿名函数,通俗点说...
  • 开篇先明义:lambda表达式,而def函数是语句代码块所以lambda其实就是一个稍微高级一点的式子而已,只不过这个式子比较长,而且还会自己进行一些逻辑上的处理而已,心里不要虚lambda也被叫做匿名函数,通俗点说...
  • 前言 本文来了解 Lambda 表达式的实现原理 ------>接上文:JDK8新特性(一)...所以在分析 Lambda 表达式原理的同时,首先我们先来了解一下 匿名内部类 的实现原理。 1.匿名内部类实现原理 ①附Demo /** * ...
  • java lambda 表达式lambda表达式和匿名内部类的区别jdk自带function方法和构造器引用 lambda表达式和匿名内部类的区别 lambda原理解析1 lambda原理解析2 用途 匿名内部类 lambda表达式 使用方面 this指向...
  • C++拾遗--lambda表达式原理

    千次阅读 2015-03-05 11:38:49
    C++拾遗--lambda表达式原理 前言 lambda表达式是在C++11新标准中提出的。在lambda表达式中,我们集中梳理了一下它的使用。现在来讨论下它的实现原理。 正文 1.函数对象 类的对象跟括号结合,表现出函数一般的...
  • 背景在使用Lamdba表达式,一直以为是内部类的方式实现的,但是一想如果每次调用都实例化一个内部类,性能肯定不好,难道Java里的lambda表达式真的是这么实现的吗?也许是该研究下原理了。正文测试代码:public class...
  • Kotlin之Lambda表达式原理及应用初探Lambda表达,可以看成是一个代码块,先来一个栗子热热身. val codeblock = { f: Float, s: String -> println("hello $s") f.toByte() }以上的codeblock就是一个代码块,也...
  • java 8 Lambda表达式原理

    2020-11-24 01:19:46
    Lambda并不是采用内部类的实现方式实现的。如果Lambda表达式使用内部类的方式,将是极为不利的。类加载需要有加载、验证、准备、解析、初始化等过程,大量的内部类将会影响应用执行的性能,并消耗Met ...
  • JDK8 使用一行 Lambda 表达式可以代替先前用匿名类五六行代码所做的事情,那么它是怎么实现的呢?从所周知,匿名类会在编译的时候生成与宿主类带上 $1, $2 的类文件,如写在 TestLambda 中的匿名类产生成类文件是 ...
  • 1、使用Lambda表达式实现多线程public static void main(String[] args) {//使用匿名内部类的方式,实现多线程new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread()....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 783
精华内容 313
关键字:

lambda表达式原理