Почему для лямбда-перевода требуется генерация статического метода?
Лямбда-перевод - это двухэтапный процесс, Один: дезауринг лямбда в статический метод в том же классе.
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 */
}
}
Ответы
Ответ 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 в методах экземпляра, не использующих переменные экземпляра, объявляет статический метод
Два первых довольно логичны. Почему вы ожидаете, что статический член получит доступ к члену экземпляра? То же самое для метода экземпляра.
Реальный вопрос: почему метод экземпляра, не использующий какие-либо переменные экземпляра, объявляет статический метод?
Ну, это также касается соображений производительности и безопасности, упомянутых Тагиром.