为什么lambda转换需要生成静态方法?

Lambda转换是一个两步过程, 一个是 :将lambda放入同一个类中的静态方法中。

public class Main { public static void main(String[] args) { Runnable r = () -> System.out.println("Hello"); System.out.println(Arrays.asList(Main.class.getDeclaredMethods())); } } 

[ private static void Main.lambda $ main $ 0() ,public static void Main.main(java.lang.String [])]

:生成实现function接口的类。

 System.out.println("A class has been generated: " + r.getClass()); System.out.println("That implements a Functional Interface: " + Arrays.asList(r.getClass().getInterfaces())); 

已生成一个类:类Main $$ Lambda $ 1/149928006

它实现了一个function接口:[interface java.lang.Runnable]

问题这种静态方法需要什么? 为什么不能将lambda主体直接放入接口方法? 就像是:

 class Main$$Lambda$1 { public void run() { /* Lambda body here */ } } 

除了这里给出的正确答案(因为当前的方案更有效,减少lambdas的捕获/连接成本和减少代码重复),还有一些其他原因,为什么你的想法根本没有意义。

  • 字节码首先来自哪里? lambda代理类是在运行时生成的,而不是在编译时生成的。 如果我们将字节码填充到代理类中,它必须来自某个地方。 这意味着我们必须将它放入捕获类文件中,然后将复制到代理类中。 在这里,它只是生活在捕获类中,我们已经完成了。
  • 访问控制。 如果lambda主体调用私有方法怎么办? 通过将其伪装到捕获类中,它会自动获取捕获类的访问控制上下文(它在逻辑上是它的一部分。)如果我们将字节码放在代理类中,我们必须做额外的魔术来给它正确的访问控制上下文。

因为这种方式实际上更便宜。 在第一次调用期间动态生成方法中的lambda比通过类加载器加载单独的类更好。 在内部它使用UNSAFE.defineAnonymousClass ,它比正常更轻量级。 这样的“lambda-class”不受任何类加载器的约束,因此在不再需要时可以很容易地进行垃圾收集。 此外,我想有计划使这种机制更轻,更快。 对于普通的匿名类,这是不可能的,因为从JVM的角度来看,这些类与通常的类没有区别,而且更加沉重。

你的考试不完整。

 public class Lambda { private String hello = "Hello from instance"; public static void main(String[] args) { Runnable r = () -> System.out.println("Hello"); for (Method m: Lambda.class.getDeclaredMethods()) { System.out.println(m); } } public void instanceMethodWithAccessToInstanceVariables(){ Runnable r = () -> System.out.println(hello); } public void instanceMethodWithoutAccessToInstanceVariables(){ Runnable r = () -> System.out.println("Hello from instance"); } } 

这导致以下结果:

 public void Lambda.instanceMethodWithAccessToInstanceVariables() public void Lambda.instanceMethodWithoutAccessToInstanceVariables() private static void Lambda.lambda$instanceMethodWithoutAccessToInstanceVariables$2() private void Lambda.lambda$instanceMethodWithAccessToInstanceVariables$1() private static void Lambda.lambda$main$0() public static void Lambda.main(java.lang.String[]) 

这清楚地表明了几种情况:

  • static方法中的lambdas声明静态方法
  • 实例方法中的lambdas使用实例变量声明实例方法
  • 不使用实例变量的实例方法中的lambdas声明静态方法

这两个首先是相当合乎逻辑的。 为什么期望静态成员访问实例成员? 实例方法也是如此。

真正的问题是为什么不使用任何实例变量的实例方法声明了静态方法?

嗯,这也是出于Tagir提到的性能和安全原因。