Data, Sealed & Value Classes Quiz
KOTLIN › Types & Classes
Which of these does a Kotlin data class NOT generate automatically?
- equals() and hashCode()
- copy()
- A no-argument constructor
- componentN() functions
Answer: A no-argument constructor
Data classes generate equals/hashCode/toString/copy/componentN. A parameterless constructor only exists if every primary constructor parameter has a default value.
A data class has 'name' in its primary constructor and 'var age' declared in the class body. Two instances have the same name but different age. Are they equal via ==?
- Yes, body properties are ignored by equals()
- No, because different age values make them unequal
- No, == compares object references in data classes
- It does not compile when age is declared outside ctor
Answer: Yes, body properties are ignored by equals()
Generated equals()/hashCode() use only primary constructor properties, so a body property like age is excluded and the instances compare equal.
When should you prefer a sealed class over an enum?
- When you need a fixed set of constants that all share the same structure
- When each case must carry its own distinct properties or state
- When you want the lowest possible memory footprint
- When the type must be usable as a constant in annotations
Answer: When each case must carry its own distinct properties or state
Enums fit a fixed set of identical-shape constants, while a sealed hierarchy models a closed set of types that can each hold different data.
Which is a valid data class declaration?
- abstract data class Foo(val x: Int, val y: Int)
- sealed data class Foo(val x: Int, val y: Int)
- data class Point(x: Int, y: Int)
- data class Point(val x: Int, val y: Int)
Answer: data class Point(val x: Int, val y: Int)
A data class cannot be abstract or sealed, and its primary constructor must have at least one val/var parameter, so only Point(val x, val y) is valid.
Where must the direct subclasses of a sealed class be declared?
- Anywhere in the codebase, with no module or package limit
- In any module, as long as the package name matches exactly
- In the same module and package as the sealed class
- Only as classes nested inside the sealed class itself
Answer: In the same module and package as the sealed class
Kotlin requires direct subclasses of a sealed class or interface to reside in the same module and package, which is what makes the hierarchy closed.
Why can a 'when' expression over a sealed type be exhaustive without an else branch?
- The compiler can see every direct subtype at compile time
- Sealed classes are compiled into enums behind the scenes
- Kotlin never requires an else branch in a when expression
- Sealed types turn off type checking inside when branches
Answer: The compiler can see every direct subtype at compile time
Because the hierarchy is closed, the compiler sees every direct subtype and can confirm all are handled; otherwise the when fails to compile.
In which usage is a @JvmInline value class kept unboxed (inlined to its underlying value)?
- Stored as a generic type argument, such as in List<Foo>
- Used through its nullable form, written Foo?
- Passed through an interface type that Foo implements
- Passed directly as its own non-nullable type Foo
Answer: Passed directly as its own non-nullable type Foo
A value class is inlined when used directly as its own non-nullable type; nullable, generic, and interface/supertype uses force boxing.
For data class User(val name: String, val tags: MutableList<String>), you run val b = a.copy() and then b.tags.add("x"). What is the state of a.tags?
- a.tags stays unchanged because copy() deep-copies each property
- It does not compile because a MutableList cannot be a data class field
- a.tags also has "x" because copy() is shallow and shares the list
- a.tags becomes null after the copy, since the list reference is reset
Answer: a.tags also has "x" because copy() is shallow and shares the list
copy() performs a shallow copy, so reference-typed properties like the list are shared between the original and the copy; mutating the shared list is visible through both.
Given data class Point(val x: Int, val y: Int), what decides which property is bound to a in 'val (a, b) = point'?
- Primary-constructor order, via component1()/component2()
- Alphabetical ordering of the declared property names
- A name match between variable a and a property also named a
- The order in which those properties were first accessed elsewhere
Answer: Primary-constructor order, via component1()/component2()
Destructuring is positional: it calls component1(), component2(), and so on in primary-constructor order, so variable names are irrelevant and reordering constructor params silently changes bindings.
Which member is permitted inside the body of a @JvmInline value class?
- A second property that declares and keeps its own backing field
- An additional stored mutable var with a backing field inside
- Extending an open superclass from the body of the value class
- A computed property with a custom getter and no backing field
Answer: A computed property with a custom getter and no backing field
A value class may hold only the single property in its primary constructor; body members are allowed only if they have no backing field, such as computed properties, and it cannot extend classes.
Which statement about sealed interfaces is correct?
- A sealed interface cannot make a when expression exhaustive.
- A single class can implement several sealed interfaces at once.
- All implementations of a sealed interface must be nested inside it.
- Implementations of a sealed interface may live in any module.
Answer: A single class can implement several sealed interfaces at once.
Because a class can implement many interfaces but extend only one class, sealed interfaces let a type participate in overlapping closed hierarchies, while implementations still must stay in the same module.
Which capability comes built in with every Kotlin enum constant but has no automatic equivalent for sealed-class subtypes?
- Implementing an interface on each subtype, with no extra enum support
- Declaring a constructor with parameters for each constant or subtype
- ordinal, name, plus generated entries/values() for all cases
- Being usable in a when expression without needing an else branch
Answer: ordinal, name, plus generated entries/values() for all cases
Enums automatically expose ordinal, name, and an entries/values() enumeration of all constants; sealed hierarchies provide no built-in case listing. Both can implement interfaces, take constructor parameters, and appear in when.
On the JVM/Android, what is required to declare a value class in modern Kotlin?
- Just the value keyword, as in value class X(val v: Int)
- The @JvmInline annotation plus the value keyword
- The inline modifier placed directly on the class declaration
- Annotating the single property with @JvmStatic in the class
Answer: The @JvmInline annotation plus the value keyword
On the JVM the value keyword must be paired with @JvmInline; the older standalone 'inline class' syntax is deprecated and value alone will not compile there.
Why can a data class not be declared 'open'?
- Subclasses break the generated equals()/hashCode()/copy()
- The open modifier is only ever permitted on interface declarations
- Data classes live in a memory region that forbids inheritance
- It actually can be open starting with the Kotlin 2.0 release
Answer: Subclasses break the generated equals()/hashCode()/copy()
Subclassing would make the auto-generated equality and copy semantics inconsistent across the hierarchy, so data classes are effectively final and cannot be open, abstract, sealed, or inner.