深入理解lambda的奥秘

JDK1.6和JDK1.8是主流的两个大版本,目前市场上用的最多最多的依然是JDK1.8。所以,我们有必要聊一聊Java8的一些新特性。

  1. 深入理解lambda的奥秘
  2. 深入理解Stream之原理剖析
  3. 深入理解Stream之foreach源码解析
  4. 深入浅出NPE神器Optional
  5. 谈谈接口默认方法与静态方法
  6. 深入浅出重复注解与类型注解
  7. 深入浅出JVM元空间metaspace

今天我们先来聊聊 深入理解lambda的奥秘 。

为什么会出现函数式编程

在数学中,函数是有输入量、输出量的一套计算方案。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而 函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。

面向对象编程是对数据进行抽象;而函数式编程是对行为进行抽象。

lambda表达式是什么

lambda表达式也可称为 闭包 ,是一个 匿名函数 ,是对匿名函数的简写形式,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递),可以写出 更简洁 、 更灵活 的代码。

作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

lambda表达式目标定位

lambda表达式的目标定位:预定义使用了 @ FunctionalInterface 注释的函数式接口,自带一个未实现的函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。

例如,若一个方法接收Runnable、Comparator或者 Callable 接口,都有单个抽象方法,可以传入lambda表达式。

类似的,如果一个方法接受声明于 java.util.function 包内的接口,里面的每一个接口都标明类注解@FunctionalInterface,并且只有一个未实现的函数。例如常见的 Predicate、Function、Consumer 或 Supplier,那么可以向其传lambda表达式。

lambda表达式写法组成

  1. 形参列表:形参列表允许省略类型,如果形参列表中只有一个参数,形参列表的圆括号也可以省略;
  2. 箭头(->):通过英文画线和大于符号组成;
  3. 代码块:如果代码块只有一条语句,花括号可以省略。Lambda 代码块只有一条 return 语句,可以省略 return 关键字,Lambda 表达式会自动返回这条语句的值作为返回值。

说到这里,相信大家已经比较明确lambda表达式了。接下来我们看看如何运用它吧。

lambda的常见使用

更深入的理解

先来看几个case

在编译器内部,会将lambda表达式翻译成私有方法,执行invokedynamic指令。我们可以使用javap -c -v查看字节码文件,当然也可以使用Idea的插件来查看字节码。

jclasslib plugin:

javap -c -v:(代码块较长,不想关注细节可直接跳过此模块)

Classfile /Users/lige/IdeaProjects/javahomeProject/out/production/javahomeProject/com/ligejishu/lambda/LambdaDemo02.class
  Last modified 2022-8-7; size 2654 bytes
  MD5 checksum c76a7d3ce0088c93f0dc265b8eee28de
  Compiled from "LambdaDemo02.java"
public class com.ligejishu.lambda.LambdaDemo02
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
    #1 = Methodref          #30.#54       // java/lang/Object."<init>":()V
    #2 = Class              #55           // java/lang/Thread
    #3 = InvokeDynamic      #0:#60        // #0:run:()Ljava/lang/Runnable;
    #4 = Methodref          #2.#61        // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
    #5 = Methodref          #2.#62        // java/lang/Thread.start:()V
    #6 = Class              #63           // java/util/ArrayList
    #7 = Methodref          #6.#54        // java/util/ArrayList."<init>":()V
    #8 = String             #64           // a
    #9 = InterfaceMethodref #17.#65       // java/util/List.add:(Ljava/lang/Object;)Z
   #10 = String             #66           // c
   #11 = String             #67           // b
   #12 = InterfaceMethodref #17.#68       // java/util/List.stream:()Ljava/util/stream/Stream;
   #13 = InvokeDynamic      #1:#72        // #1:test:(Ljava/lang/String;)Ljava/util/function/Predicate;
   #14 = InterfaceMethodref #73.#74       // java/util/stream/Stream.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
   #15 = Methodref          #75.#76       // java/util/stream/Collectors.toList:()Ljava/util/stream/Collector;
   #16 = InterfaceMethodref #73.#77       // java/util/stream/Stream.collect:(Ljava/util/stream/Collector;)Ljava/lang/Object;
   #17 = Class              #78           // java/util/List
   #18 = InvokeDynamic      #2:#82        // #2:compare:()Ljava/util/Comparator;
   #19 = InterfaceMethodref #17.#83       // java/util/List.sort:(Ljava/util/Comparator;)V
   #20 = InvokeDynamic      #3:#87        // #3:accept:()Ljava/util/function/Consumer;
   #21 = InterfaceMethodref #17.#88       // java/util/List.forEach:(Ljava/util/function/Consumer;)V
   #22 = Fieldref           #89.#90       // java/lang/System.out:Ljava/io/PrintStream;
   #23 = String             #91           // 123,
   #24 = Methodref          #92.#93       // java/io/PrintStream.print:(Ljava/lang/String;)V
   #25 = String             #94           // ,
   #26 = Methodref          #92.#95       // java/io/PrintStream.println:()V
   #27 = String             #96           // test print.
   #28 = Methodref          #92.#97       // java/io/PrintStream.println:(Ljava/lang/String;)V
   #29 = Class              #98           // com/ligejishu/lambda/LambdaDemo02
   #30 = Class              #99           // java/lang/Object
   #31 = Utf8               <init>
   #32 = Utf8               ()V
   #33 = Utf8               Code
   #34 = Utf8               LineNumberTable
   #35 = Utf8               LocalVariableTable
   #36 = Utf8               this
   #37 = Utf8               Lcom/ligejishu/lambda/LambdaDemo02;
   #38 = Utf8               main
   #39 = Utf8               ([Ljava/lang/String;)V
   #40 = Utf8               args
   #41 = Utf8               [Ljava/lang/String;
   #42 = Utf8               list
   #43 = Utf8               Ljava/util/List;
   #44 = Utf8               filterList
   #45 = Utf8               LocalVariableTypeTable
   #46 = Utf8               Ljava/util/List<Ljava/lang/String;>;
   #47 = Utf8               lambda$main$1
   #48 = Utf8               (Ljava/lang/String;)V
   #49 = Utf8               it
   #50 = Utf8               Ljava/lang/String;
   #51 = Utf8               lambda$main$0
   #52 = Utf8               SourceFile
   #53 = Utf8               LambdaDemo02.java
   #54 = NameAndType        #31:#32       // "<init>":()V
   #55 = Utf8               java/lang/Thread
   #56 = Utf8               BootstrapMethods
   #57 = MethodHandle       #6:#100       // 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;
   #58 = MethodType         #32           //  ()V
   #59 = MethodHandle       #6:#101       // invokestatic com/ligejishu/lambda/LambdaDemo02.lambda$main$0:()V
   #60 = NameAndType        #102:#103     // run:()Ljava/lang/Runnable;
   #61 = NameAndType        #31:#104      // "<init>":(Ljava/lang/Runnable;)V
   #62 = NameAndType        #105:#32      // start:()V
   #63 = Utf8               java/util/ArrayList
   #64 = Utf8               a
   #65 = NameAndType        #106:#107     // add:(Ljava/lang/Object;)Z
   #66 = Utf8               c
   #67 = Utf8               b
   #68 = NameAndType        #108:#109     // stream:()Ljava/util/stream/Stream;
   #69 = MethodType         #107          //  (Ljava/lang/Object;)Z
   #70 = MethodHandle       #5:#110       // invokevirtual java/lang/String.equals:(Ljava/lang/Object;)Z
   #71 = MethodType         #111          //  (Ljava/lang/String;)Z
   #72 = NameAndType        #112:#113     // test:(Ljava/lang/String;)Ljava/util/function/Predicate;
   #73 = Class              #114          // java/util/stream/Stream
   #74 = NameAndType        #115:#116     // filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
   #75 = Class              #117          // java/util/stream/Collectors
   #76 = NameAndType        #118:#119     // toList:()Ljava/util/stream/Collector;
   #77 = NameAndType        #120:#121     // collect:(Ljava/util/stream/Collector;)Ljava/lang/Object;
   #78 = Utf8               java/util/List
   #79 = MethodType         #122          //  (Ljava/lang/Object;Ljava/lang/Object;)I
   #80 = MethodHandle       #5:#123       // invokevirtual java/lang/String.compareTo:(Ljava/lang/String;)I
   #81 = MethodType         #124          //  (Ljava/lang/String;Ljava/lang/String;)I
   #82 = NameAndType        #125:#126     // compare:()Ljava/util/Comparator;
   #83 = NameAndType        #127:#128     // sort:(Ljava/util/Comparator;)V
   #84 = MethodType         #129          //  (Ljava/lang/Object;)V
   #85 = MethodHandle       #6:#130       // invokestatic com/ligejishu/lambda/LambdaDemo02.lambda$main$1:(Ljava/lang/String;)V
   #86 = MethodType         #48           //  (Ljava/lang/String;)V
   #87 = NameAndType        #131:#132     // accept:()Ljava/util/function/Consumer;
   #88 = NameAndType        #133:#134     // forEach:(Ljava/util/function/Consumer;)V
   #89 = Class              #135          // java/lang/System
   #90 = NameAndType        #136:#137     // out:Ljava/io/PrintStream;
   #91 = Utf8               123,
   #92 = Class              #138          // java/io/PrintStream
   #93 = NameAndType        #139:#48      // print:(Ljava/lang/String;)V
   #94 = Utf8               ,
   #95 = NameAndType        #140:#32      // println:()V
   #96 = Utf8               test print.
   #97 = NameAndType        #140:#48      // println:(Ljava/lang/String;)V
   #98 = Utf8               com/ligejishu/lambda/LambdaDemo02
   #99 = Utf8               java/lang/Object
  #100 = Methodref          #141.#142     // 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;
  #101 = Methodref          #29.#143      // com/ligejishu/lambda/LambdaDemo02.lambda$main$0:()V
  #102 = Utf8               run
  #103 = Utf8               ()Ljava/lang/Runnable;
  #104 = Utf8               (Ljava/lang/Runnable;)V
  #105 = Utf8               start
  #106 = Utf8               add
  #107 = Utf8               (Ljava/lang/Object;)Z
  #108 = Utf8               stream
  #109 = Utf8               ()Ljava/util/stream/Stream;
  #110 = Methodref          #144.#145     // java/lang/String.equals:(Ljava/lang/Object;)Z
  #111 = Utf8               (Ljava/lang/String;)Z
  #112 = Utf8               test
  #113 = Utf8               (Ljava/lang/String;)Ljava/util/function/Predicate;
  #114 = Utf8               java/util/stream/Stream
  #115 = Utf8               filter
  #116 = Utf8               (Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
  #117 = Utf8               java/util/stream/Collectors
  #118 = Utf8               toList
  #119 = Utf8               ()Ljava/util/stream/Collector;
  #120 = Utf8               collect
  #121 = Utf8               (Ljava/util/stream/Collector;)Ljava/lang/Object;
  #122 = Utf8               (Ljava/lang/Object;Ljava/lang/Object;)I
  #123 = Methodref          #144.#146     // java/lang/String.compareTo:(Ljava/lang/String;)I
  #124 = Utf8               (Ljava/lang/String;Ljava/lang/String;)I
  #125 = Utf8               compare
  #126 = Utf8               ()Ljava/util/Comparator;
  #127 = Utf8               sort
  #128 = Utf8               (Ljava/util/Comparator;)V
  #129 = Utf8               (Ljava/lang/Object;)V
  #130 = Methodref          #29.#147      // com/ligejishu/lambda/LambdaDemo02.lambda$main$1:(Ljava/lang/String;)V
  #131 = Utf8               accept
  #132 = Utf8               ()Ljava/util/function/Consumer;
  #133 = Utf8               forEach
  #134 = Utf8               (Ljava/util/function/Consumer;)V
  #135 = Utf8               java/lang/System
  #136 = Utf8               out
  #137 = Utf8               Ljava/io/PrintStream;
  #138 = Utf8               java/io/PrintStream
  #139 = Utf8               print
  #140 = Utf8               println
  #141 = Class              #148          // java/lang/invoke/LambdaMetafactory
  #142 = NameAndType        #149:#153     // 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;
  #143 = NameAndType        #51:#32       // lambda$main$0:()V
  #144 = Class              #154          // java/lang/String
  #145 = NameAndType        #155:#107     // equals:(Ljava/lang/Object;)Z
  #146 = NameAndType        #156:#157     // compareTo:(Ljava/lang/String;)I
  #147 = NameAndType        #47:#48       // lambda$main$1:(Ljava/lang/String;)V
  #148 = Utf8               java/lang/invoke/LambdaMetafactory
  #149 = Utf8               metafactory
  #150 = Class              #159          // java/lang/invoke/MethodHandles$Lookup
  #151 = Utf8               Lookup
  #152 = Utf8               InnerClasses
  #153 = 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;
  #154 = Utf8               java/lang/String
  #155 = Utf8               equals
  #156 = Utf8               compareTo
  #157 = Utf8               (Ljava/lang/String;)I
  #158 = Class              #160          // java/lang/invoke/MethodHandles
  #159 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #160 = Utf8               java/lang/invoke/MethodHandles
{
  public com.ligejishu.lambda.LambdaDemo02();
    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 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/ligejishu/lambda/LambdaDemo02;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, 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: invokevirtual #5                  // Method java/lang/Thread.start:()V
        15: new           #6                  // class java/util/ArrayList
        18: dup
        19: invokespecial #7                  // Method java/util/ArrayList."<init>":()V
        22: astore_1
        23: aload_1
        24: ldc           #8                  // String a
        26: invokeinterface #9,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        31: pop
        32: aload_1
        33: ldc           #10                 // String c
        35: invokeinterface #9,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        40: pop
        41: aload_1
        42: ldc           #11                 // String b
        44: invokeinterface #9,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        49: pop
        50: aload_1
        51: invokeinterface #12,  1           // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;
        56: ldc           #8                  // String a
        58: invokedynamic #13,  0             // InvokeDynamic #1:test:(Ljava/lang/String;)Ljava/util/function/Predicate;
        63: invokeinterface #14,  2           // InterfaceMethod java/util/stream/Stream.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
        68: invokestatic  #15                 // Method java/util/stream/Collectors.toList:()Ljava/util/stream/Collector;
        71: invokeinterface #16,  2           // InterfaceMethod java/util/stream/Stream.collect:(Ljava/util/stream/Collector;)Ljava/lang/Object;
        76: checkcast     #17                 // class java/util/List
        79: astore_2
        80: aload_1
        81: invokedynamic #18,  0             // InvokeDynamic #2:compare:()Ljava/util/Comparator;
        86: invokeinterface #19,  2           // InterfaceMethod java/util/List.sort:(Ljava/util/Comparator;)V
        91: aload_1
        92: invokedynamic #20,  0             // InvokeDynamic #3:accept:()Ljava/util/function/Consumer;
        97: invokeinterface #21,  2           // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
       102: return
      LineNumberTable:
        line 16: 0
        line 19: 15
        line 20: 23
        line 21: 32
        line 22: 41
        line 23: 50
        line 26: 80
        line 27: 91
        line 34: 102
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0     103     0  args   [Ljava/lang/String;
           23      80     1  list   Ljava/util/List;
           80      23     2 filterList   Ljava/util/List;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
           23      80     1  list   Ljava/util/List<Ljava/lang/String;>;
           80      23     2 filterList   Ljava/util/List<Ljava/lang/String;>;
}
SourceFile: "LambdaDemo02.java"
InnerClasses:
     public static final #151= #150 of #158; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #57 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:
      #58 ()V
      #59 invokestatic com/ligejishu/lambda/LambdaDemo02.lambda$main$0:()V
      #58 ()V
  1: #57 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:
      #69 (Ljava/lang/Object;)Z
      #70 invokevirtual java/lang/String.equals:(Ljava/lang/Object;)Z
      #71 (Ljava/lang/String;)Z
  2: #57 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:
      #79 (Ljava/lang/Object;Ljava/lang/Object;)I
      #80 invokevirtual java/lang/String.compareTo:(Ljava/lang/String;)I
      #81 (Ljava/lang/String;Ljava/lang/String;)I
  3: #57 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:
      #84 (Ljava/lang/Object;)V
      #85 invokestatic com/ligejishu/lambda/LambdaDemo02.lambda$main$1:(Ljava/lang/String;)V
      #86 (Ljava/lang/String;)V

由此可见,如果使用lambda表达式使用方法引用,则编译器不会生成私有方法;如果lambda表达式没有使用方法引用,而是自定义函数实现,则编译器会生成一个内部私有方法。

在这个示例代码块上,能够发现case1模块与case3的foreach模块,都是自定义了函数实现,所以编译器生成了两个私有方法,名称分别为:<lambda$main main$1>

总结

  1. lambda表达式强调具体实现,而不是用什么形式做
  2. lambda表达式使得代码更简洁、灵活
  3. lambda表达式也称之为闭包、匿名函数
  4. 一般情况下可使用lambda表达式的接口都会标明类注解@FunctionalInterface,当然,不标明也没问题
  5. 一般情况下如果使用lambda表达式,使用 java.util.function 包下的接口大多数都能满足要求
  6. 使用lambda表达式,如果是自定义函数编译器在编译阶段会生成内部私有方法,如果是使用方法引用则不会生成内部私有方法
  7. lambda表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在lambda内部修改定义在域外的变量。

来源:https://blog.51cto.com/u_15538087/5624861

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值