В последней части наконец-то перейдём к лямбда-выражениям.
Что нам уже известно:
🔸 Лямбды не приводят к созданию анонимных классов
🔸 В java 7 появились invokedynamic и MethodHandle API для поддержки динамических языков. Но эти штуки отлично подошли для некоторых java фич, в том числе лямбда-выражений.
Если не читали - прочитайте, тогда этот пост будет понятней.
Итак, разработчик пишет код с лямбда-выражением
stream().filter(x -> x<3)В байткоде на месте вызова лямбды появляется инструкция
invokedynamic #2, 0Следуем по указателям из Constant Pool и попадаем в секцию бутстрап-методов. Видим там вызов
LambdaMetafactory.metafactory(…)
Этот фабричный метод возвращает объект CallSite, внутри которого спрятался MethodHandle. Больше в байткоде нет информации. Понятно, что работа с лямдой - это какое-то перенаправление, а вся магия описана в классе LambdaMetafactory.
Класс лежит в JDK, идём туда и видим:
1️⃣ С помощью ASM библиотечки создаётся внутренний класс. Он реализует интерфейс Predicate и называется как-то так:
class Smth$$Lambda$14/0x0000000800c02460Класс динамический и существует только в памяти.
2️⃣ Создаётся объект этого класса и привязывается к MethodHandle внутри ConstantCallSite.
3️⃣ Все обращения к лямбде переадресуется этому объекту.
❓Чем это лучше анонимных классов?
✅ Класс создаётся и загружается динамически. Кажется, что это долго и сложно, но на деле получается быстрее, чем загрузка файла с диска
✅ Есть кэширование
✅ Новый класс наравне со всеми участвует в JIT оптимизациях
❓Почему нельзя вынести лямбду в отдельный метод и просто вызывать его?
Чтобы встроить решение в остальной java код. Если в коде пишется
Predicate p = i -> x<3;То ожидается, что p - это объект. Его можно куда-то передать и вызвать его методы.
Логика вроде "назовём это объектом, но на самом деле это метод" - это слишком большое усложнение. Так не работает. Новые фичи должны вписываться в систему, а не обходить её.
❓Зачем столько переадресаций? Почему из бутстрап метода надо идти в другой класс? Нельзя было сделать встроенную инструкцию?
Текущий код LambdaMetaFactory со временем изменится. Добавится больше кэширования, семантика лямбд расширится в следующих версиях java. Если вся логика описана в JDK, то гораздо легче поддерживать совместимость версий.
Такая вот эпопея. Я весь процесс поняла не с первого раза и даже не со второго. Поэтому решила провести вас маленькими шажками, и описать всё максимально просто.