Подтвердить что ты не робот

Почему для лямбда-перевода требуется генерация статического метода?

Лямбда-перевод - это двухэтапный процесс, Один: дезауринг лямбда в статический метод в том же классе.

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

Два: генерация класса, реализующего функциональный интерфейс.

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

Создан класс: class Main $$ Lambda $1/149928006

Это реализует функциональный интерфейс: [interface java.lang.Runnable]

Вопрос: Какая потребность в этом статическом методе? Почему тело лямбда не может быть помещено непосредственно в метод интерфейса? Что-то вроде:

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

Ответ 1

В дополнение к правильным ответам, приведенным здесь (поскольку действующая схема более эффективна, уменьшая затраты на захват/связь для lambdas и уменьшая дублирование кода), есть еще несколько причин, почему ваша идея просто не имеет смысла.

  • Откуда бы появился байт-код? Класс lambda proxy генерируется во время выполнения, а не время компиляции. Если бы мы использовали байт-код в прокси-классе, это должно было бы произойти откуда-то. Это означало бы, что нам пришлось бы поместить его в файл класса захвата, а затем скопировать его в прокси-класс. Здесь он просто живет в классе захвата, и мы закончили.
  • Контроль доступа. Что, если тело лямбда вызывает частный метод? Путем обесцвечивания его в класс захвата он автоматически получает контекст управления доступом класса захвата (который он логически является частью.) Если мы поместим байт-код в прокси-класс, нам придется сделать дополнительную магию, чтобы дать ему правильный контекст управления доступом.

Ответ 2

Потому что так оно и есть на самом деле дешевле. Генерация лямбда из метода "на лету" во время первого вызова лучше, чем загрузка отдельного класса через загрузчик классов. Внутри он использует UNSAFE.defineAnonymousClass, который имеет более легкий класс, чем обычно. Такой "лямбда-класс" не связан ни с одним загрузчиком классов, поэтому его можно легко собрать в мусор, когда он больше не нужен. Также я предполагаю, что есть планы сделать этот механизм еще более легким и быстрым. Для обычного анонимного класса это было бы невозможно, так как с точки зрения JVM такие классы не отличаются от обычных классов и гораздо более тяжелыми.

Ответ 3

Ваш тест неполный.

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

Это ясно показывает несколько случаев:

  • lambdas в статическом методе объявляет статический метод
  • lambdas в методах экземпляра с использованием переменных экземпляра declare методы экземпляра
  • lambdas в методах экземпляра, не использующих переменные экземпляра, объявляет статический метод

Два первых довольно логичны. Почему вы ожидаете, что статический член получит доступ к члену экземпляра? То же самое для метода экземпляра.

Реальный вопрос: почему метод экземпляра, не использующий какие-либо переменные экземпляра, объявляет статический метод?

Ну, это также касается соображений производительности и безопасности, упомянутых Тагиром.