Why does lambda translation need generation of a s

2019-01-24 01:18发布

问题:

Lambda translation is a two step process, One: desugaring the lambda into a static method in same class.

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[])]

Two: generation of a class that implements the Functional Interface.

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

A class has been generated: class Main$$Lambda$1/149928006

That implements a Functional Interface: [interface java.lang.Runnable]

Question: What is the need of this static method? Why can't the lambda body be put directly into the interface method? Something like:

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

回答1:

In addition to the correct answers given here (because the current scheme is more efficient, reducing capture/linkage costs for lambdas and reducing code duplication), there are a few other reasons why your idea simply doesn't make sense.

  • Where would the bytecode come from in the first place? The lambda proxy class is generated at runtime, not compile time. If we were to stuff the bytecode into the proxy class, it would have to come from somewhere. That would mean we'd have to put it into the capturing class file and then copy it into the proxy class. Here, it just lives in the capturing class and we're done.
  • Access control. What if the lambda body calls a private method? By desugaring it into the capturing class, it automatically acquires the access control context of the capturing class (which it is logically a part of.) If we put the bytecode in the proxy class, we'd have to do additional magic to give it the right access control context.


回答2:

Because this way it's actually cheaper. Generating a lambda from the method on the fly during the first invocation is better than loading a separate class via class loader. Internally it uses UNSAFE.defineAnonymousClass which is more light-weight class than normal. Such "lambda-class" is not bound to any class loader, so can be easily garbage-collected when it's no longer necessary. Also I guess there are plans to make this mechanism even more light-weight and faster. For normal anonymous class this would not be possible as from the point of JVM such classes don't differ from usual classes and much more heavy.



回答3:

Your test is incomplete.

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");
  }
}

This results in the following:

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[])

This clearly shows several cases:

  • lambdas in static method declare static method
  • lambdas in instance methods using instance variables declare instance methods
  • lambdas in instance methods not using instance variables declare static method

The two first are rather logical. Why would you expect a static member to access an instance member? Same for the instance method.

The real question is why does an instance method not using any instance variables declare a static method?

Well, this is also for performance and safety reasons mentioned by Tagir.