ProgrammingBackend developer

What is an 'inline class' (value class) in Kotlin? What are they used for, how to use them correctly, what are the limitations and features of such classes? Please provide code examples.

Pass interviews with Hintsage AI assistant

Answer

inline class (from Kotlin 1.5 — value class) allows creating wrappers around primitives without creating a separate object at runtime. This is used to enhance type safety without the overhead of memory allocation. Under the hood, such objects can be compiled to the corresponding primitive.

Example:

@JvmInline value class UserId(val id: String) fun printUserId(userId: UserId) { println(userId.id) }
  • Only one property (the primary field) is allowed.
  • No-argument constructor is not possible.
  • The class cannot contain state (e.g., var fields), only val.
  • Cannot inherit other classes, but can implement interfaces.
  • Interaction with Java code may be non-obvious — boxing/unboxing may occur.

Using inline/value classes is important for typing identifiers, money, units of measurement, etc.

Trick Question

Can a value class have more than one property?

Answer: No. A value class can contain only one property in the primary constructor.

Example of erroneous code:

@JvmInline value class Money(val amount: Int, val currency: String) // Compilation error

Examples of real errors due to ignorance of the subtleties of the topic


Story

In a currency exchange, an inline class was used to describe the amount and currency. They tried to add two fields to the value class, received a compilation error, and spent some time trying to work around the limitation. Eventually, they decided to create a separate data class.


Story

When integrating with an external Java library, the inline class sometimes unexpectedly converted to an object (boxing), which affected performance. After analyzing the documentation, they replaced it with a regular value object.


Story

In a microservices project, they used value classes as IDs in the API. One of the services returned a string directly, while another returned a value class, which led to a serialization conflict with Jackson. They fixed it by explicitly mapping between the ID and the string.