首先来看一段简单的代码
@FunctionalInterface
interface ILambdaCaculator{
int result(int a,int b);
}
public class LambdaTest {
public static void main(String[] args) {
System.out.println("add :"+LambdaUse((a,b)-> a+b,12,14));
}
public static int LambdaUse(ILambdaCaculator lambda,int a,int b){
return lambda.result(a, b);
}
}
代码在static main方法中使用了一个Lambda表达式,然后完成a+b的操作。
如果大家对lambda表达式不熟悉的话,可以先阅读我的另一篇博文,Lambda使用详解。另外呢,还是建议大家先看我的另一篇博文,java内部类相关的底层实现。
那这个lambda表达式编译器是怎么处理的呢?
我们反编译一个LambdaTest类的class文件,一看究竟。
命令:
javap -p LambdaTest.class
Compiled from "LambdaTest.java"
public class share_work_impl.LambdaTest {
public share_work_impl.LambdaTest();
public static void main(java.lang.String[]);
public static int LambdaUse(share_work_impl.ILambdaCaculator, int, int);
private static int lambda$0(int, int);
}
发现很神奇的地方了, private static int lambda$0(int, int);这个方法我们没有定义啊,怎么来的呢?其实这个方法就是对应这个(a,b)-> a+b,12,14)lambda表达式的,也就是说,编译器会为每一个lambda表达式生成一个私有的lambda$x方法,这个private static int lambda$0(int, int);方法是静态的。难道所有的表达式都是生成静态、私有的lambda$x方法吗?还是因为我的这个lambda表达式是写在main这个静态方法中才导致是生成静态方法的??
我们把表达式改到普通成员方法中看看。
代码改成如下:
@FunctionalInterface
interface ILambdaCaculator {
int result(int a, int b);
}
public class LambdaTest {
public static void main(String[] args) {
LambdaTest test=new LambdaTest();
test.LambdaUse(12,23);
}
public void LambdaUse(int a,int b) {
ILambdaCaculator lambdaCaculator=(x,y)->x+y;
System.out.println("a+b="+lambdaCaculator.result(a, b));
}
}
我们接着反编译LambdaTest类的class文件。
命令:
javap -p LambdaTest.class
Compiled from "LambdaTest.java"
public class share_work_impl.LambdaTest {
public share_work_impl.LambdaTest();
public static void main(java.lang.String[]);
public void LambdaUse(int, int);
private static int lambda$0(int, int);
}
咦,发现也是静态的耶。难道就真的是全部的lambda表达式都会生成一个静态的方法和它相对吗?
在给出一段代码:
@FunctionalInterface
interface ILambdaCaculator {
int result(int a, int b);
}
public class LambdaTest {
private int c=123;
public static void main(String[] args) {
LambdaTest test=new LambdaTest();
test.LambdaUse(12,23);
}
public void LambdaUse(int a,int b) {
ILambdaCaculator lambdaCaculator=(x,y)->x+y+c;
System.out.println("a+b+c="+lambdaCaculator.result(a, b));
}
}
再给出反编译后的代码。
命令:
javap -p LambdaTest.class
Compiled from "LambdaTest.java"
public class share_work_impl.LambdaTest {
private int c;
public share_work_impl.LambdaTest();
public static void main(java.lang.String[]);
public void LambdaUse(int, int);
private int lambda$0(int, int);
}
什么!!!!private int lambda$0(int, int);
不是静态的,到底发生了什么?大家仔细看上面的代码。大家是不是感觉和之前的差不多,感觉现在就像是找两张图片的不同。哈哈哈。
其实改动的地方就是
@FunctionalInterface
interface ILambdaCaculator {
int result(int a, int b);
}
public class LambdaTest {
private int c=123;//增加了一个成员变量
public static void main(String[] args) {
LambdaTest test=new LambdaTest();
test.LambdaUse(12,23);
}
public void LambdaUse(int a,int b) {
ILambdaCaculator lambdaCaculator=(x,y)->x+y+c;//在lambda表达式中调用了这个成员变量
System.out.println("a+b+c="+lambdaCaculator.result(a, b));
}
}
哦,原来这样。当lambda表达式调用了外部的成员变量或者方法的时候,生成的lambda$x方法就只是私有的了,并没有静态。
那lambda表达式是只是生成了这个方法吗?想想,肯定不是,要是只有这个方法,谁来调用呢?肯定还生成了其他东西,没错,还成了class,但是默认这个class不会写在文件中,所以我们要给编译器配置一下。
在eclipse中选中java文件,右键。
然后在VM arguments中输入-Djdk.internal.lambda.dumpProxyClasses
然后执行文件。
然后发现在项目的与src同文件夹下多了一个文件夹,这个文件夹就是lambda表达式所在的包名,里面就存放了对应lambda表达式的类。
发现生成了LambdaTest$$Lambda$1.class这么一个class,你会发现,它的这个格式和内部类差别好大,LambdaTest$$Lambda$1.class比内部类多了$$Lambda这个部分,可能是为了区分吧,这个我也不太清楚。
因为我们的lambda表达式访问了外面类的成员,是不是这个class也会拿到外部类的引用呢?看看这个class吧。
命令:
javap -p LambdaTest\$\$Lambda\$1.class
final class share_work_impl.LambdaTest$$Lambda$1 implements share_work_impl.ILambdaCaculator {
private final share_work_impl.LambdaTest arg$1;
private share_work_impl.LambdaTest$$Lambda$1(share_work_impl.LambdaTest);
private static share_work_impl.ILambdaCaculator get$Lambda(share_work_impl.LambdaTest);
public int result(int, int);
}
通过这句话 private final share_work_impl.LambdaTest arg$1;
确认是拿到了外部类的一个引用,那又回到了内部类的问题,这个引用怎么来另一个class中访问它的私有的成员的方法的呢?会不会像内部类那样通过静态的access$x方法呢?
我们还是使用命令看一下:
命令:
javap -p -c LambdaTest\$\$Lambda\$1.class
final class share_work_impl.LambdaTest$$Lambda$1 implements share_work_impl.ILambdaCaculator {
private final share_work_impl.LambdaTest arg$1;
private share_work_impl.LambdaTest$$Lambda$1(share_work_impl.LambdaTest);
Code:
0: aload_0
1: invokespecial #13 // Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #15 // Field arg$1:Lshare_work_impl/LambdaTest;
9: return
private static share_work_impl.ILambdaCaculator get$Lambda(share_work_impl.LambdaTest);
Code:
0: new #2 // class share_work_impl/LambdaTest$$Lambda$1
3: dup
4: aload_0
5: invokespecial #19 // Method "<init>":(Lshare_work_impl/LambdaTest;)V
8: areturn
public int result(int, int);
Code:
0: aload_0
1: getfield #15 // Field arg$1:Lshare_work_impl/LambdaTest;
4: iload_1
5: iload_2
6: invokespecial #27 // Method share_work_impl/LambdaTest.lambda$0:(II)I
9: ireturn
}
主要看public int result(int, int);方法下的第6行,invokespecial #27 // Method share_work_impl/LambdaTest.lambda$0:(II)I。可以看到,这里是直接调用这个LambdaTest.lambda$0方法的。它不是像内部类使用静态的access$x方法这种方式,那它是怎么调用私有的方法和成员的呢?这个问题,我现在不能给出明确的答案,如果大家知道了,要记得留言告诉我哦。还有一个问题,
这个代码:
@FunctionalInterface
interface ILambdaCaculator {
int result(int a, int b);
}
public class LambdaTest {
public int c=123;
public static void main(String[] args) {
LambdaTest test=new LambdaTest();
test.LambdaUse(12,23);
}
public void LambdaUse(int a,int b) {
ILambdaCaculator lambdaCaculator=(x,y)->x+y+c;
System.out.println("a+b+c="+lambdaCaculator.result(a, b));
}
private int getC(){
return c;
}
}
反编译代码
final class share_work_impl.LambdaTest$$Lambda$1 implements share_work_impl.ILambdaCaculator {
private final share_work_impl.LambdaTest arg$1;
private share_work_impl.LambdaTest$$Lambda$1(share_work_impl.LambdaTest);
private static share_work_impl.ILambdaCaculator get$Lambda(share_work_impl.LambdaTest);
public int result(int, int);
}
那这个反编译代码的private static share_work_impl.ILambdaCaculator get$Lambda(share_work_impl.LambdaTest);这个方法的目的是干什么的?
如果上面的代码中的lambda表达式没有访问外部类的成员,会有这个方法吗?
@FunctionalInterface
interface ILambdaCaculator {
int result(int a, int b);
}
public class LambdaTest {
public int c=123;
public static void main(String[] args) {
LambdaTest test=new LambdaTest();
test.LambdaUse(12,23);
}
public void LambdaUse(int a,int b) {
ILambdaCaculator lambdaCaculator=(x,y)->x+y;
System.out.println("a+b+c="+lambdaCaculator.result(a, b));
}
private int getC(){
return c;
}
}
那生成的Lambda$x方法就是静态的。那生成的class呢?
反编译一下。
final class share_work_impl.LambdaTest$$Lambda$1 implements share_work_impl.ILambdaCaculator {
private share_work_impl.LambdaTest$$Lambda$1();
Code:
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>":()V
4: return
public int result(int, int);
Code:
0: iload_1
1: iload_2
2: invokestatic #18 // Method share_work_impl/LambdaTest.lambda$0:(II)I
5: ireturn
}
发现一个问题,就是没有访问外部类成员的时候,就没有了这个方法private static share_work_impl.ILambdaCaculator get$Lambda(share_work_impl.LambdaTest);上面访问了外部类成员变量就有这个方法,那这个方法的真正作用是什么呢??
我们来看看这个方法干了什么事情。
private static share_work_impl.ILambdaCaculator get$Lambda(share_work_impl.LambdaTest);
Code:
0: new #2 // class share_work_impl/LambdaTest$$Lambda$1
3: dup
4: aload_0
5: invokespecial #19 // Method "<init>":(Lshare_work_impl/LambdaTest;)V
8: areturn
简单的说就是创建了当前class所代表类的实例并向上转型成share_work_impl.ILambdaCaculator接口类型。
为什么lambda表达式访问了外部类成员或者方法就有这个 private static share_work_impl.ILambdaCaculator get$Lambda(share_work_impl.LambdaTest);
方法,而如果没有访问外部类成员就没有这个方法。如果是说限制外部类不能调用lambda表达式里面的内容,那应该两种情况都要这种方法啊。我目前还没有想明白这个问题。如果大家知道的,要记得留言给我哦,还有上面那个问题哦。