реализующих функции интерфейса в фабрике? Т.е., например, есть вот такой код:
public interface Interface {
public fun functionality()
}
public fun fabric(realization: () -> Unit): Interface =
object : Interface {
override fun functionality() = realization()
}
public inline fun inlineFabric(crossinline realization: () -> Unit): Interface =
object : Interface {
override fun functionality() = realization()
}
public fun usage() : Interface = fabric { println(1) }
public fun inlineUsage() : Interface = inlineFabric { println(1) }
Есть ли преимущество у inlineFabric перед fabric? А у fabric перед inlineFabric?
Скорее всего, в inline случае у object будет каждый раз разный класс. В этом контексте, вместо realization стоит использовать слово implementation.
Не думаю, вроде уже смотрел. Но утверждать не буду
А как оно технически может работать? Ну, в каком пакете оно будет создавать класс для объекта? Оно ж inline, поэтому эффект должен быть такой же, как создание объекта по каждому месту использования. Вряд ли компилятор ищет и объединяет классы с одинаковым содержимым
Ну в данном случае метод один, он может мониторить. Но на самом деле тут не получится, так как тело метода всегда разное - так что новый класс
Тело одинаковое. Implementation (аргумент) же в этом примере не инлайнится.
А там лямбда кроссинлайн, ну тогда может отследить вызовы инлайн функции и использовать один класс. Не могу проверить увы. Пс: я про jvm, про другие платформы не знаю
Почему? Там же crossinline
Он не гарантирует инлайнинг. Вопрос конечно может ли вообще такое инлайнить
Да где вы это берёте. Всё гарантируется, как и с обычным inline. Просто запрещены non-local return.
Да ? Ну тогда каждый раз новый класс - сомнительная оптимизация. Как мне казалось кросинлайн говорит что компилятор сам выберет инлайнит или нет.
Нет, это про другое. Это не инлайнинг в JIT, где компилятор что-то решает сам, тут всё однозначно и как написано в коде.
Проверил на 1.6.20, jvmTarget = "1.8" TL;DR: на jvm разницы почти нет. В конкретном сценарии (fabric(impl: () -> Unit): Interface = object {…}) inline + crossinline немного лучше, но в обоих случаях количество классов одно и то же. Разница такая: a) В случае no inline для каждого использования генерируется класс, который implements kotlin.jvm.functions.Function (по сути, тело лямбды), и объект этого класса передаётся в fabric (которое оборачивает ещё одним объетком). Если лябмда не захватывает переменных, то используются INSTANCE объекты: fabric(…INSTANCE), но это всё равно не спасает от аллокаций, т.к. fabric-то внутри всё равно создаёт объект. б) В случае inline+crossinline тело лямбды действительно инлайнится прямо в создавамый объект. Т.е. для каждого использования создаётся свой класс, со своим телом-реализацией. И, разумется, сам метод inlineFabric уже не вызывается, т.е. выглядит получше. —— Если же лямбду передавать не as is, а объектом, то поведение +- одинаковое: в обоих случаях используется один класс (количество классво не зависит от количества таких вызовов): fun test4(abc: () -> Unit) { inlineFabric(abc) // нужно заметить, вот тут ничего не инлайнится, да и непонятно как вообще можно проинлайнить такое без закрытого мира ) } fun test4(abc: () -> Unit) { fabric(abc) }
Спасибо, это как раз то, что интересовало!
Обсуждают сегодня