skip to content

Data Class Constraints

Data classes need at least one val or var in the primary constructor and cannot be abstract, open, sealed, or inner. Knowing that copy and componentN cover only constructor properties is what keeps you from designing around them incorrectly.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What are the requirements on a data class's primary constructor, and why won't `data class Empty()` compile?

level: juniorimportance: must knowfreq 70%

answer

  1. At least one primary-constructor param
  2. Every param must be val or var
  3. data class Empty() does not compile
  4. Bare param (no val/var) is a compile error
  5. Body properties are excluded from generated members

basics

~20 s

A data class must declare a primary constructor with at least one parameter, and every parameter must be marked val or var. An empty parameter list, or a plain parameter without val/var, is a compile error.

solid answer

~40 s

The Kotlin compiler enforces two rules on a data class primary constructor: (1) it must have at least one parameter, and (2) every primary-constructor parameter must be marked `val` or `var` so it becomes a property. `data class Empty()` fails because there are zero parameters, so the compiler cannot generate meaningful `equals`/`hashCode`/`toString`/`componentN`/`copy`. Likewise `data class P(x: Int)` fails because `x` is a constructor parameter, not a property — you must write `data class P(val x: Int)`. These rules exist because the generated members operate over the constructor properties; with no properties there is nothing to compare, hash, or destructure. Secondary constructors and an explicit body are still allowed, but they cannot satisfy the at-least-one-property requirement.

code

kotlin · 8 lines
kotlin
data class Empty()          // compile error: needs >= 1 param
data class Bad(x: Int)      // compile error: x must be val/var
data class Good(val x: Int) // OK

val a = Good(1)
val b = Good(1)
println(a == b)             // true (value equality)
println(a.copy(x = 2))      // Good(x=2)

go deeper

for a junior

Knows a data class needs at least one val/var parameter and can fix the compile error.

for a middle

Explains why the rules exist (generated members need properties) and that body properties are excluded.

for a senior

Connects the constraint to equals/hashCode/copy/componentN semantics and discusses the body-vs-constructor property distinction precisely.

for a principal

Frames the constraints as compiler-enforced invariants protecting value-semantics contracts, and reasons about API design implications when modeling value objects.

## What a data class is A `data class` is a class whose primary purpose is to hold data. The `data` keyword tells the Kotlin compiler to auto-generate `equals()`, `hashCode()`, `toString()`, `componentN()` functions (for destructuring), and `copy()` based on the properties declared in the **primary constructor**. ## The primary constructor The primary constructor is the parameter list written right after the class name: `class Point(val x: Int, val y: Int)`. A parameter prefixed with `val` (read-only) or `var` (mutable) is not just a constructor argument — it is automatically declared as a **property** (a field with a getter, and for `var` also a setter). ## Constraint 1 — at least one parameter The primary constructor must declare **at least one** parameter: ```kotlin data class Empty() // ERROR: Data class must have at least one primary constructor parameter data class User(val id: Int) // OK ``` Without properties, the generated members are meaningless (every instance would be equal, hash identically, and have nothing to destructure), so the compiler rejects it. ## Constraint 2 — every primary-constructor parameter must be val or var Each parameter in the primary constructor must be marked `val` or `var`: ```kotlin data class P(x: Int) // ERROR: Data class primary constructor parameters must be 'val' or 'var' data class P(val x: Int) // OK ``` A bare parameter (`x: Int`) is only visible inside `init` blocks and property initializers, not a property — so it cannot participate in the generated members. ## What is still allowed - A class **body** with extra members, methods, secondary constructors. - Properties declared **in the body** (not the constructor) — but these are **excluded** from `equals`/`hashCode`/`toString`/`copy`/`componentN`. ```kotlin data class User(val id: Int) { var lastSeen: Long = 0 // NOT part of equals/hashCode/copy } ``` ## Mnemonic summary - Need ≥1 primary-constructor parameter. - Each such parameter must be `val`/`var`. - Body properties are legal but ignored by generated members.

  • Can a data class have additional properties declared in its body?
    Yes, but they are excluded from the generated equals, hashCode, toString, copy, and componentN; only primary-constructor properties participate.
  • Does a data class with one val parameter still get all generated members?
    Yes — one property is enough; you get equals, hashCode, toString, component1, and copy.

A data class is like a labeled box: it needs at least one labeled compartment (a val/var property), or there is nothing to compare or copy.

saying these in an interview costs you the question

  • Claiming a data class can have zero parameters
  • Thinking a bare parameter (no val/var) is fine in a data class
  • Believing body-declared properties affect equals/hashCode
  • Saying the rule is about secondary constructors, not the primary one
  • Confusing 'parameter' with 'property' and assuming all params auto-become properties without val/var

context

open as a page

Which class modifiers are forbidden on a data class (abstract, open, sealed, inner), and why?

level: middleimportance: must knowfreq 55%

basics

~10 s

A data class cannot be abstract, open, sealed, or inner. It is implicitly final and standalone. You can still make it implement interfaces or be a member of (but not inner to) another class.

open as a page

How do `componentN()` and `copy()` decide which properties they cover, and what happens to properties declared in the class body?

level: middleimportance: should knowfreq 45%

basics

~10 s

Only the properties listed in the primary constructor are covered. copy() has a parameter per constructor property, and componentN() exposes them in order. Properties declared in the class body are ignored by both.

open as a page

What happens to `copy()` and `componentN()` if a data class's primary-constructor property is private, and what visibility pitfalls follow?

level: seniorimportance: nice to knowfreq 25%

basics

~10 s

The generated copy() and componentN() still cover every constructor property, including private ones, and copy() itself is public. So a private property can leak out through copy(), which can undermine your intended encapsulation.

open as a page

When modeling immutable value types, when do data class constraints push you toward a non-data class, a value class, or a @JvmRecord, and what constraints differ?

level: principalimportance: nice to knowfreq 15%

basics

~20 s

Data classes can't be open/abstract/sealed/inner and always generate copy(), which leaks private state and boxes single fields. For invariants use a plain class plus factory; for one wrapped value use @JvmInline value class; for Java record interop use @JvmRecord on the data class.

open as a page