Давно хотела написать о том, как выглядят лямбды внутри JVM.
Во-первых, мало кто об этом знает. Есть несколько статей и видео с конференций, но они достаточно сложные.
Во-вторых, это интересно. Для ответа на вопрос нужно представлять, как работает JVM и собрать вместе несколько концептов.
В-третьих, время не стоит на месте. Java развивается, а многие до сих пор не в курсе изменений 10летней давности.
Как устроены лямбды - вопрос непростой, поэтому будем идти небольшими шагами. В этом посте посмотрим на возможные реализации лямбд и обсудим, что с ними не так.
Часть 1: внешний вид. Как лямбды будут выглядеть в коде?
Первая догадка - использовать опыт других языков и ввести в JVM тип function:
fun(s: Integer): boolean { return s<3; }
Легко для понимания, сложно для реализации. Java - ООП язык, всё в JVM заточено под работу объектов и примитивов. Добавить новый тип данных - чудовищно огромная работа. Как функция будет хранится, вызываться и взаимодействовать с другими объектами? Будет ли она работать с дженериками? Тысячи вопросов и сложностей.Второй вариант: пусть лямбда реализует интерфейс с одним методом:
Predicate p = i -> i<3;Выглядит по-джавовски и отлично впишется в текущую систему. Существующие библиотеки будут работать с лямбдами по умолчанию.
Но какой объект будет присвоен переменной p?
Часть 2: реализация
Здесь тоже два варианта.
Первый - сделать внутренний класс с одним методом и создать его экземпляр.
✅ Простая реализация
❌ Придётся компилировать и загружать класс для каждой лямбды, а при каждом вызове создавать новый объект.
С учётом того, что лямбды станут основой Stream API и использоваться для обработки данных - решение ужасное и непроизводительное.
Второй вариант - компилировать код лямбды в отдельный метод и подставлять в исходный код MethodHandle.
Мы рассмотрим MethodHandle на следующей неделе, но если кратко - это указатель на метод. Компилируем исходный пример в:
public boolean lambda(Integer i) { return i<3; }
MethodHandle mh = LDC["lambda"];
И подставляем mh в исходный код:...stream().filter(mh)...В теории решение отличное, на практике - неподходящее.
❌ Теряется информация о входных и выходных типах, приходится проверять их в рантайме
❌ Библиотечные методы не принимают на вход MethodHandle
Как же сделаны лямбды?
Об этом поговорим на следующей неделе:
▫️ Сначала вернёмся во времена java 6 и посмотрим как вызываются методы в JVM
▫️ Изучим новую байткод инструкцию java 7 и чуть глубже обсудим MethodHandle
После этого говорить про лямбды будет легко и понятно.