lambda表达式的底层实现

首先来看一段简单的代码

@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表达式里面的内容,那应该两种情况都要这种方法啊。我目前还没有想明白这个问题。如果大家知道的,要记得留言给我哦,还有上面那个问题哦。

















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值