从Java的lambda表达式加深理解:面向对象、封装、匿名内部类;面向过程(函数式)、闭包、匿名函数
作者:互联网
三个Main类:
第一个Main类:
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.test();
}
void test() {
int i = 2;
i++;
((Runnable) () -> {
int j=i;
System.out.println(j);
}).run();
}
}
这个Main类并不能通过编译,因为这违反了闭包原则:
提示:
Variable used in lambda expression should be final or effectively final
也就是说,java的lambda表达式只能从外部语境中引入不变量,所以其实java的lambda表达式只是一个伪闭包实现,因为它不clone外部变量表中的变量到自身的局部变量表,相反地,它对外部的变量提出约束要求 (对程序员的脑洞也提出了约束要求) 。
第二个Main类:
public class Main2 {
private int i=2;
public static void main(String[] args) {
Main2 main2 = new Main2();
main2.test();
}
void test(){
((Runnable) () -> System.out.println(i++)).run();
main2.i++;
}
}
那这个例子能不能通过编译并正常运行呢?
答案是能的。
为什么能呢?简单的猜测一二,或许是匿名内部类持有了Main类的引用吧,所以javap -c -p -l Main2,反编译验证一下:
Compiled from "Main2.java"
public class forTest.forclosureAndLambda.Main2 {
private int i;
public forTest.forclosureAndLambda.Main2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_2
6: putfield #2 // Field i:I
9: return
LineNumberTable:
line 3: 0
line 4: 4
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LforTest/Main2;
public static void main(java.lang.String[]);
Code:
0: new #3 // class forTest/Main2
3: dup
4: invokespecial #4 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #5 // Method test:()V
12: return
LineNumberTable:
line 6: 0
line 7: 8
line 8: 12
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
8 5 1 main2 LforTest/Main2;
void test();
Code:
0: aload_0
1: invokedynamic #6, 0 // InvokeDynamic #0:run:(LforTest/Main2;)Ljava/lang/Runnable;
6: invokeinterface #7, 1 // InterfaceMethod java/lang/Runnable.run:()V
11: aload_0
12: dup
13: getfield #2 // Field i:I
16: iconst_1
17: iadd
18: putfield #2 // Field i:I
21: return
LineNumberTable:
line 10: 0
line 11: 11
line 12: 21
LocalVariableTable:
Start Length Slot Name Signature
0 22 0 this LforTest/Main2;
private void lambda$test$0(); // 注意这段 //
Code:
0: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: dup
5: getfield #2 // Field i:I
8: dup_x1
9: iconst_1
10: iadd
11: putfield #2 // Field i:I
14: invokevirtual #9 // Method java/io/PrintStream.println:(I)V
17: return
LineNumberTable:
line 10: 0
LocalVariableTable:
Start Length Slot Name Signature
0 18 0 this LforTest/Main2;
}
显然,猜测是错误的,因为根本没有预料中的Main2$1.class文件,取而代之的是 lambda$test$0()
这么个匿名函数,这说明lambda表达式并不是简单的java匿名内部类语法糖,而是完全不同的另一种东西——匿名函数,这其实是背离面向对象编程思想的函数式编程思想实现。
第三个Main类:
作为比较,看看传统的匿名内部类写法:
public class Main3 {
private int i = 0;
public static void main(String[] args) {
Main3 main3 = new Main3();
main3.test();
}
void test() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(i++);
}
};
runnable.run();
}
}
javap -c -p -l Main3$1康一康是什么情况:
Compiled from "Main3.java"
class forJDK.forClosureAndLambda.Main3$1 implements java.lang.Runnable {
final forJDK.forClosureAndLambda.Main3 this$0;
forJDK.forClosureAndLambda.Main3$1(forJDK.forClosureAndLambda.Main3);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LforJDK/forClosureAndLambda/Main3;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 12: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LforJDK/forClosureAndLambda/Main3$1;
0 10 1 this$0 LforJDK/forClosureAndLambda/Main3; // 注意这里 //
public void run();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LforJDK/forClosureAndLambda/Main3;
7: invokestatic #4 // Method forJDK/forClosureAndLambda/Main3.access$008:(LforJDK/forClosureAndLambda/Main3;)I
10: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
13: return
LineNumberTable:
line 15: 0
line 16: 13
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 this LforJDK/forClosureAndLambda/Main3$1;
}
这就和在第二个情况中的猜测是一致的,匿名内部类留有Main类的引用。
这是面向对象编程的思想实现,而为什么是持有Main类的引用而不是Main类的字段的引用,则是因为要满足封装。
这三个Main类整理完,我大概就加深了几个重要的概念之间的关系和区别理解:
传统java匿名内部类:面向对象编程思想,封装。
java的lambda表达式(匿名函数):面向过程编程思想(函数式编程思想),伪闭包。
标签:闭包,java,函数,Main2,Main3,匿名,line,Main,public 来源: https://blog.csdn.net/Eglusaxie/article/details/112110281