Являются ли входящие функции Kotlin менее дорогими, чем анонимные классы Java?

Heads up: Я пишу часть этого из памяти, поэтому у меня могут быть некоторые из неправильных понятий.


Java имеет возможность писать анонимную функцию. Это полезно, когда у вас есть интерфейс прослушивателя для какого-то события. В качестве примера:

button.setOnClickListener(new View.OnClickListener(View v) {
    @Override
    public void onClick(View v) {
        // handle the action here
    }
});

Анонимный слушатель будет скомпилирован как класс, который называется чем-то вроде OnClickListener$1.class. Это базовое дизайнерское решение языка Java. Все это объект, даже анонимные функции.

Это становится проблемой, когда вы хотите написать более функционально управляемую базу кода. Большое количество анонимных классов создает большой класс, что может быть проблемой на таких ограниченных платформах, как Android.

В функциях Kotlin гораздо более первый класс с точки зрения исходного кода. Мой вопрос заключается в том, что Kotlin более эффективно компилирует эти функции до байтового кода, чем Java, с анонимными классами, или я буду сталкиваться с теми же проблемами, что и большой класс в Java?

Спасибо,

Ответ 1

Короткий ответ да, встроенные функции Kotlin довольно дешевы.

Когда вызов встроенной функции компилируется, lambdas, переданные вызову, вставляются в тело функции, которое, в свою очередь, включено на сайт вызова. Это позволяет компилятору не генерировать никаких дополнительных классов или методов для лямбда-тел.

Компиляция встроенной функции

Один из слайды о компиляции конструкций Kotlin @yole.  К сожалению, я нашел запись только на русском языке. Другие слайды также представляют определенный интерес, вы можете найти больше о не-inlined lambdas там.

В общем, код Kotlin, который использует встроенные функции с lambdas, работает быстрее, чем идентичный код Java с lambdas или Streams. Вся привязка кодов выполняется во время компиляции, и нет накладных расходов на запуск вызовов виртуальных методов, а также увеличение количества методов, что имеет значение для Android.

Недостатком чрезмерной инкрустации является рост размера кода: общая часть байт-кода встроенного тела функции фактически дублируется на сайтах вызовов. Кроме того, вложение осложняет отладку, поскольку номера строк и стек вызовов кода будут отличаться от того, что было в исходном файле. Хотя поддержка IDE может помочь здесь.

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

Ответ 2

Kotlin имеет ключевое слово inline. Если вы используете это ключевое слово, он не только встраивает эту функцию, но и обрабатывает тело лямбда так, как будто это всего лишь вложенный уровень видимости, так что вы можете return от нее!

Пример (прямо из документов)

fun foo() {
    inlineFunction {
        return // OK: the lambda is inlined
    }
}

Обратитесь к документам за дополнительной информацией:

https://kotlinlang.org/docs/reference/inline-functions.html

Edit:

Чтобы уточнить ваш точный вопрос о производительности, это первый абзац из документов:

Использование функций более высокого порядка накладывает определенные штрафы во время выполнения: каждая функция является объектом, и она захватывает замыкание, то есть те переменные, к которым обращаются в теле функции. Выделение памяти (как для объектов-функций, так и классов) и виртуальных вызовов представляет собой служебные данные времени выполнения.

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

Итак, насколько я могу сказать "да", он встроит функцию и удалит все служебные данные, которые в противном случае были бы наложены.

Однако, похоже, это относится только к функциям, которые вы объявляете как inline.