TGViewer
Android under the hood Android under the hood @android_under_the_hood · 1.53K subscribers
Post #139 360
Kotlin lambdas, часть II.

Начиная с Kotlin 2.0 лямбды перестали напрямую компилироваться в анонимные классы:
val twice = { x: Int -> x * 2 }

// тело лямбды становиться статическим методом
private static final int main$lambda$0(int x) {
return x * 2;
}

// для получения реализации одного из интерфейсов FunctionX используется специальная JVM инструкция, куда передается необходимый интерфейс (Function1) и ссылка на Bootstrap метод
invokedynamic invoke:()Lkotlin/jvm/functions/Function1;


Инструкция invokedynamic устроена так что не привязана к конкретной реализации и поэтому нуждается в Bootstrap методе, который должен отдать нужную реализацию:
// В Bootstrap метод передается интерфейс FunctionX, сигнатура его метода invoke и ссылка на метод, реализующий этот интерфейс (статический метод который является телом лямбды)
CallSite site = LambdaMetafactory.metafactory(
lookup,
"invoke",
methodType(Function1.class),
methodType(Object.class, Object.class),
methodHandle(MainKt::main$lambda$0),
methodType(Integer.class, Integer.class)
);

// CallSite это контейнер, содержащий MethodHandle который либо является готовым объектом лямбды либо конструктором который можно использовать для ее создания. CallSite нужен для того чтобы один и тот же вызов invokedynamic не делал снова вызов Bootstrap метода.
repeat(1000) {
// тут не будет тысяча аллокаций, так как CallSite после первого вызова invokedynamic уже связан с этим местом вызова и содержит уже готовый объект лямбды.
val f = { x: Int -> x * 2 }
use(f)
}


Функция LambdaMetafactory.metafactory() под капотом собирает класс, реализующий интерфейс лямбды через ClassFile API, такие классы нельзя найти через Class.forName(), также для них не создаются отдельные .class файлы, что для лямбд является неплохой такой оптимизацией.

В итоге цепочка следующая:

1) Генерируется static метод с телом лямбды method0.
2) Вызывается invokedynamic куда передается нужный интерфейс FunctionX, ссылка на Bootstrap метод и при необходимости захваченные переменные.
3) Bootstrap метод через ClassFile API собирает класс, реализующий нужный FunctionX интерфейс, а в тело кладет ссылку на статический метод method0.
4) Создается обьект лямбды либо ссылка на его конструктор если есть захваченные переменные, все это оборачивается в CallSite, который нужен чтобы связать invokedynamic c определенным местом вызова и не вызывать повторно Bootstrap метод.
5) Получаем наконец объект лямбды, если был захват переменных invokedynamic сам передаст их значения в конструктор лямбды.
  • 👍 5
  • ❤ 3
  • 🔥 3
More from @android_under_the_hood
  1. Oct 4, 2026Всем привет, хочу поделиться проектом, который запустил 1 октября — ЗдесьЯ. Это цифровая с…
  2. Oct 2, 2026Kotlin lambdas, часть I. До версии Kotlin 2.0 лямбды компилировались в анонимные классы, р…
  3. Sep 27, 2026Делегат свойства в Kotlin. В Kotlin есть конструкция, которой нет в JVM: var name by NameD…
  4. Sep 23, 2026val vs var под капотом. На уровне Kotlin все просто: val x = 10 var y = 20 y = 30 // компи…
  5. Sep 20, 2026Возвращение Прошло уже полгода с последней записи на канале. За это время в моей жизни про…
  6. Feb 5, 2026Пару фактов о Go, часть II. 4) Вместо Kotlin Nullability указатели как в С/C++, то есть об…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →