Value classes

Kotlin language Mid-level Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Predict the output

Which call fails to compile?

@JvmInline value class UserId(val raw: String)
@JvmInline value class OrderId(val raw: String)

fun loadUser(id: UserId) = "user ${id.raw}"

fun main() {
    println(loadUser(UserId("u1")))
    println(loadUser(OrderId("o1")))
}
Options
Hint 1 of 1

Are UserId and OrderId the same type?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A loadUser(OrderId("o1"))

Correct. OrderId and UserId are different types.

Why not the others?
  • B loadUser(UserId("u1")). That's exactly the type loadUser expects.
  • C Neither: both wrap a String. Value classes are distinct types at compile time.
  • D Both: value classes can't be function parameters. They can.

30-second answer

@JvmInline value class UserId(val raw: String) wraps a single value in its own type, so you can't pass an OrderId where a UserId is expected. At runtime the compiler usually uses the underlying value directly, so there's no extra object. It's type safety for IDs, units and validated strings at almost no cost.

Practise saying it · 30 s. No recording, just a timer

Explain value classes.

Say your answer out loud, then check which of these points you covered:

  • Single-value wrapper type
  • Usually no runtime object
  • Type safety for IDs and units
  • Boxed when nullable or generic
  • typealias gives no safety

Deep dive

The value is boxed when used as a nullable type, a generic type argument or through an interface.

Value classes can have an init block for validation and member functions.

Function names that take value classes are mangled for Java callers.

They wrap exactly one constructor property (others must be computed), and === isn't allowed on them.

Common mistakes

  • Expecting zero cost when used in a List or as Any.
  • Using a typealias and expecting type safety (aliases are the same type).
  • Calling these functions from Java without knowing about mangling.

Senior take & follow-ups

Contrast with typealias; that's what interviewers probe.

Follow-up questions

Think of your answer first, then open the hint.

  1. Why not a typealias?

    Show hint

    A typealias is just another name for the same type; it doesn't stop mixing IDs.

  2. When is a value class boxed?

    Show hint

    When used as nullable, generic, or via an interface type.

Similar on iOS: Phantom types Swift uses wrapper structs or phantom types for the same type safety.

Suggest a fix if something here is wrong or out of date.