4

深入理解lambda的奥秘

 1 year ago
source link: https://blog.51cto.com/u_15538087/5624861
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

大家好,我是李哥。

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表达式是什么

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的常见使用

深入理解lambda的奥秘_javaee_02

更深入的理解

先来看几个case

深入理解lambda的奥秘_lambda表达式_03

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

jclasslib plugin:

深入理解lambda的奥秘_lambda表达式_04

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$mainmain$1>

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

谈到lambda表达式,我们对于Stream需要单独拎出来聊一聊:

 ​深入理解Stream之原理剖析​

 ​深入理解Stream之foreach源码解析​

深入理解lambda的奥秘_java_06

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK