ПрограммированиеKotlin разработчик / Performance engineer

Как работают inline property accessors (инлайновые геттеры и сеттеры) в Kotlin? Какие есть особенности и тонкости их использования, как они влияют на производительность и где могут возникнуть неожиданные ошибки? Приведите пример.

Проходите собеседования с ИИ помощником Hintsage

Ответ

Kotlin позволяет помечать геттеры и сеттеры свойств модификатором inline. Это даёт компилятору право инлайнить (встраивать) код аксессора непосредственно в места вызова для оптимизации производительности.

Пример:

val foo: Int inline get() = expensiveCalculation()
  • Из-за инлайна функция-геттер не вызывает накладных расходов на вызов (например, в циклах), если код аксессора короткий.
  • Инлайн возможен только для геттеров незапоминающих (stateless); не всей логики разрешён инлайн.
  • Нельзя использовать inline для set/gеттера, если в нём содержатся reified generic-параметры или лямбда-выражения с кросс-линковкой.

Лучшие практики: использовать inline accessor только для очень коротких, частовызваемых выражений без побочных эффектов.


Вопрос с подвохом

Если к свойству с инлайновым геттером/сеттером добавить annotation с рефлексией (например, использовать через KProperty), будет ли работать inline?

Ответ: Нет. Если свойство используется через reflection API или ссылается на KProperty, компилятор не сможет встроить геттер/сеттер, и они останутся обычными методами. Инлайн происходит только при прямом обращении в коде.


История

Потеря производительности из-за работы рефлексии с inline getter:

Переписали горячее свойство на inline getter, рассчитывая избавиться от лишних вызовов. Позже добавили валидацию через KProperty — в итоге вызовы стали происходить через reflection, полностью нивелируя преимущество inline-акцессора.


История

Нежеланные побочные эффекты при inlining:

Инлайновый геттер делал логирование:

inline get() { println("accessed!") return field }

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


История

Ломка ABI из-за изменения inline-property:

Поменяли логику inline-геттера в библиотеке, не пересобрав зависимые модули, клиенты продолжили использовать старую сигнатуру — расширенный ABI плюс инлайнинг привёл к несовместимости и скрытому падению клиентов при обновлении.