skip to content

When would you choose a @JvmInline value class over a single-field data class? What members can a value class still have?

level: middleimportance: should knowfreq 50%

answer

  1. value class = 1 field, no copy/destructuring, no alloc
  2. data class = N fields, copy + componentN, always allocates
  3. value class CAN have init, computed vals, functions, interfaces
  4. value class CANNOT have var/second field/lateinit/extend class
  5. both auto-gen equals/hashCode/toString

basics

~20 s

Use a value class when you want a typed wrapper around one value with no runtime cost. A single-field data class always creates a real object. Value classes can still have functions, computed properties, init checks, and implement interfaces.

solid answer

~40 s

Choose a `@JvmInline value class` when you need a distinct domain type around exactly one value and want to avoid allocation — e.g. `UserId`, `Email`, `Meters`. A single-property `data class` gives similar ergonomics (auto `equals`/`hashCode`/`toString`/`copy`) but **always allocates a wrapper object**, so it has runtime overhead a value class avoids. A value class auto-generates `equals`/`hashCode`/`toString` based on the wrapped value too, but does **not** generate `copy` or `componentN` (no destructuring like a data class). What a value class can still have: secondary computed `val`s (no extra backing field), member functions, an `init` block for validation, and interface implementations. It cannot have `var`s, multiple constructor properties, an `init`-stored second field, lateinit, or `inner`/extend a class. Pick data class when you genuinely need multiple fields or `copy`/destructuring.

go deeper

for a junior

Knows value class avoids an object and data class makes one; vaguely aware of copy/destructuring difference.

for a middle

Cleanly contrasts allocation, copy/componentN, field count, and lists allowed members of a value class.

for a senior

Adds boxing/mangling caveats that make data class preferable in some cases and reasons about API ergonomics.

for a principal

Designs domain-type policies (when to wrap primitives, interop costs, library ABI) across a codebase.

## The core decision | Aspect | `@JvmInline value class X(val v: T)` | `data class X(val v: T)` | |---|---|---| | Runtime cost | Usually **no allocation** (unboxed to `T`) | **Always allocates** an object | | Number of properties | Exactly **one** | One or more | | Generated `equals`/`hashCode`/`toString` | Yes (from the value) | Yes | | Generated `copy()` | **No** | Yes | | `componentN()` / destructuring | **No** | Yes | | Mutability | `val` only | `val` or `var` | **Rule of thumb:** if you have a single immutable field and want a zero-cost domain type, reach for a value class. If you need multiple fields, `copy`, or destructuring, use a data class. ## Members a value class may still have Despite the single stored property, a value class is a real class and can contain: ```kotlin @JvmInline value class Email(val value: String) : Comparable<Email> { init { require("@" in value) { "invalid email" } } val domain: String // computed val, no new field get() = value.substringAfter('@') fun mask(): String = value.replace(Regex("."), "*") override fun compareTo(other: Email) = value.compareTo(other.value) } ``` Allowed: `init` block, computed (get-only) properties, member functions, interface implementation (`Comparable`, custom interfaces). ## What it cannot do - No `var` and no second stored property (only the one constructor `val`). - No `lateinit`, no `inner` modifier. - Cannot extend another class (implicitly extends `Any`). - Does not get `copy()` or `componentN()`. ## Why not just always use value class? Because it boxes (allocates) in several situations (nullable, generic argument, as `Any`/interface), and its functions are name-mangled for Java. When those costs matter or you need multiple fields, a data class is the right tool.

  • Does a value class generate copy() or componentN()?
    No. It generates equals/hashCode/toString from the wrapped value but not copy() and not componentN(), so no destructuring.
  • Can a value class implement an interface?
    Yes, it can implement interfaces (e.g. Comparable) but cannot extend a class.

saying these in an interview costs you the question

  • Saying a value class supports destructuring like a data class
  • Claiming a single-field data class is also allocation-free
  • Saying value classes can't have member functions or init blocks
  • Believing value class generates copy()
  • Recommending value class for multi-field types

context