skip to content

What are the structural constraints on a value class — how many properties can it have, and can it have ordinary properties with backing fields?

level: middleimportance: must knowfreq 60%

answer

  1. Exactly ONE val property
  2. No extra backing-field properties
  3. Computed getters + methods are OK
  4. Property must be val, not var
  5. init validation since 1.4.30

basics

~10 s

A value class must have exactly one value (one constructor property). It can't have extra stored properties — only computed ones. That single value is what it really is underneath.

solid answer

~40 s

A value class must wrap **exactly one** property, declared `val` in the primary constructor. You cannot add more constructor properties, and you cannot add ordinary class-body properties that have a **backing field** (stored state) — because at runtime the class is represented by that single underlying value, there's nowhere to store extra fields. You *can* add properties with custom getters (computed, no backing field) and you *can* add functions/methods. The single property does not have to be a primitive; it can be any type, though primitives/String give the most benefit. This single-property rule is why value classes model 'one scalar concept' — an id, an email, a money amount — rather than aggregates.

code

kotlin · 6 lines
kotlin
@JvmInline value class Email(val value: String) {
    init { require("@" in value) { "invalid email" } }
    val domain: String get() = value.substringAfter('@')   // computed, OK
    fun masked(): String = "***@" + domain                  // method, OK
    // val parts = value.split('@')   // ERROR: backing field not allowed
}

go deeper

for a junior

Knows there must be exactly one property and it is the underlying value.

for a middle

Explains backing-field properties are disallowed but computed getters and methods are fine, and ties this to the inlined runtime representation.

for a senior

Connects the constraint to immutability and the 'single scalar concept' modeling rule, and knows init validation is supported.

for a principal

Reasons about when the single-property limit should push a design toward a data class or a sealed model, and about library/API stability of value-class members.

## The one-property rule A **value class** must declare **exactly one** property in its **primary constructor**, and that property must be `val` (read-only): ```kotlin @JvmInline value class Meters(val value: Double) // OK // @JvmInline value class Point(val x: Int, val y: Int) // ERROR: needs exactly one property ``` The reason is the runtime model: the compiler **inlines** the class so that, in the common case, an instance is represented directly by its single underlying value. There is no wrapper object to hold a second field, so a second stored property is impossible. ## Backing fields are not allowed (beyond the one) A **backing field** is the hidden storage that an ordinary property uses to hold its value. Because a value class has room for only the one underlying value, you **cannot** declare additional properties that need backing fields: ```kotlin @JvmInline value class Celsius(val value: Double) { // val cached: Double = value * 2 // ERROR: would need a backing field val fahrenheit: Double get() = value * 9 / 5 + 32 // OK: computed, no backing field } ``` Properties with a **custom getter** (computed each access, no stored state) are fine, as are member functions. ## What you still get - **Methods and computed properties** — so a value class can carry behavior (e.g. `Email.domain`). - **`init` block** for validation, supported since Kotlin **1.4.30** (the prompt's 'pre-1.x' phrasing predates the stable feature; in modern Kotlin 2.x `init` is available). - **Interface implementation** — a value class may implement interfaces (it just can't extend a class). ## What you don't get - More than one stored property. - A `var` constructor property (must be `val`, immutable). - Extra stored/backing-field properties. ## Mental model Think of a value class as a **typed alias for one value plus optional behavior**: it is its single property at runtime, dressed up with a distinct type and methods. ## Keywords primary constructor, single `val`, backing field, custom getter, `init` block, computed property.

  • Can a value class hold a `var` instead of `val`?
    No. The single constructor property must be `val`. Value classes are immutable by design; their identity is the underlying value, so mutation isn't supported.
  • If you need two fields, like x and y for a point, what should you use?
    A regular `data class`. Value classes model a single scalar concept; an aggregate of two-plus fields is exactly what a data class is for.

saying these in an interview costs you the question

  • Saying a value class can have two properties if one is computed
  • Claiming you can add stored properties in the body
  • Allowing a var property in a value class
  • Confusing computed getters (allowed) with backing-field props (not)
  • Thinking value class replaces data class for multi-field types

context