ProgrammingKotlin Developer / Performance Engineer

How do inline property accessors work in Kotlin? What are the features and nuances of their usage, how do they affect performance, and where can unexpected errors occur? Provide an example.

Pass interviews with Hintsage AI assistant

Answer

Kotlin allows marking property getters and setters with the inline modifier. This gives the compiler the right to inline the accessor code directly at the call sites for performance optimization.

Example:

val foo: Int inline get() = expensiveCalculation()
  • Due to inlining, the getter function incurs no call overhead (e.g., in loops) if the accessor code is short.
  • Inlining is only possible for stateless getters; not all logic is allowed to be inlined.
  • You cannot use inline for a setter/getter if it contains reified generic parameters or lambda expressions with cross-linking.

Best practices: use inline accessor only for very short, frequently called expressions with no side effects.


Trick Question

If you add an annotation with reflection to a property with an inline getter/setter (for example, using it through KProperty), will inline still work?

Answer: No. If a property is used through the reflection API or references KProperty, the compiler will not be able to inline the getter/setter, and they will remain ordinary methods. Inlining only occurs with direct access in the code.


History

Performance loss due to reflection with inline getter:

We rewrote a hot property to an inline getter, hoping to eliminate unnecessary calls. Later, validation through KProperty was added — as a result, calls started happening through reflection, completely negating the advantage of the inline accessor.


History

Unwanted side effects when inlining:

The inline getter was doing logging:

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

This implementation led to unexpected logging in many places when the property was frequently read in different parts of the code, which cluttered the logs significantly.


History

ABI break due to changing inline property:

We changed the logic of the inline getter in the library without rebuilding dependent modules; clients continued using the old signature — extended ABI plus inlining led to incompatibility and silent client failures upon update.